Eu estava escrevendo um Makefile (no Ubuntu 20.04, se for relevante) e notei um comportamento interessante com o echo
. Pegue este Makefile simples:
test.txt:
@echo -e 'hello\nworld'
@echo -e 'hello\nworld' > test.txt
Quando eu executo make
, eu esperaria ver a mesma coisa no stdout que no test.txt, mas na verdade não vejo. Eu recebo isso no stdout:
hello
world
mas isso em test.txt:
-e hello
world
Enquanto isso, se eu remover -e
de ambas as linhas no Makefile, recebo isso no stdout:
hello\nworld
e isso em test.txt:
hello
world
Isso me fez pensar se echo
detecta o redirecionamento e se comporta de maneira diferente, mas não quando eu apenas o executo manualmente no shell /bin/echo -e 'hello\nworld' > test.txt
(que produz hello
e world
em linhas separadas, como eu normalmente esperaria). Cheguei até a confirmar que o Makefile está usando /bin/echo em vez de um shell embutido adicionando uma @echo --version
linha.
O que está acontecendo aqui?
Implementações compatíveis com UNIX
echo
são necessárias para saída-e<space>hello<newline>world<newline>
lá.Aqueles que não são compatíveis. Muitos não são, o que significa que é quase impossível usar
echo
portátil,printf
deve ser usado em vez disso .bash
'secho
, em algumas (a maioria) compilações dele, só é compatível quando você habilita as opçõesposix
expg_echo
. Esse pode ser oecho
comportamento que você estava esperando.O mesmo para o
echo
utilitário autônomo que vem com o GNUcoreutils
, que só é compatível se for invocado com$POSIXLY_CORRECT
set em seu ambiente (e for de uma versão recente o suficiente).make
normalmente é executadosh
para interpretar as linhas de comando em cada linha de ação.No entanto, a implementação GNU de
make
, como uma otimização, pode executar comandos diretamente se o código for simples o suficiente e achar que não precisa invocar um shell para interpretá-lo.Isso explica porque
echo --version
te dá/bin/echo
, masecho ... > file
precisa de um shell para realizar o redirecionamento.Você pode usar
strace -fe execve make
para ver o quemake
é executado (ou otruss
/tusc
... equivalente em seu sistema se não for Linux).Aqui, parece que, embora o seu
/bin/echo
não seja compatível, o seush
possui umecho
built-in compatível.Aqui, use
printf
se você quiser expandirecho
as sequências de escape -style:Em seu argumento de formato
printf
, entende as sequências de escape do estilo C (há uma diferença com as do estilo echo para as sequências octais\0xxx
(eco) vs (C))\xxx
Aqui, você também pode fazer:
Qual é a maneira comum e portátil de gerar vários argumentos em linhas separadas.
Outra abordagem seria adicionar:
Para o seu
Makefile
formake
invocarbash
(supondo que esteja instalado e encontrado em$PATH
) em vez desh
interpretar as linhas de comando. Ou invoquemake
comomake <target> SHELL=bash
.Isso não o tornará necessariamente mais portátil, pois, embora haja cada vez mais sistemas
bash
instalados por padrão nos dias de hoje, há alguns (como no Solaris) em quebash
é construído para que seuecho
interno se comporte da maneira padrão por padrão.Definir
SHELL
para algo diferente de/bin/sh
também tem o efeito colateral de desabilitarmake
a otimização do GNU mencionada acima, portanto, commake <target> SHELL=sh
oumake <target> SHELL=/bin//sh
, você obteria comportamentos consistentes entre as duas invocações, sem precisar adicionar uma dependência ao bash.