
O echo
in coreutils
parece ser onipresente, mas nem todo sistema o terá no mesmo lugar (normalmente /bin/echo
). Qual é a maneira mais segura de invocar isso echo
sem saber onde está?
Estou confortável com a falha do comando se o echo
binário coreutils não existir no sistema - isso é melhor do que repetir algo diferente do que eu quero.
Nota: A motivação aqui é encontrar o echo
binário,nãopara encontrar um conjunto de argumentos onde cada shellecho
construídas emé consistente. Não parece haver uma maneira de imprimir com segurança apenas um hífen por meio do echo interno, por exemplo, sem saber se você está em zsh
ou bash
.
Responder1
Observe que coreutils
é um pacote de software desenvolvido pelo projeto GNU para fornecer um conjunto de utilitários básicos Unix para sistemas GNU. Você só encontrarácoreutilsecho
pronto para uso em sistemas GNU ( Debian
, trisquel
, Cygwin
, Fedora
, CentOS
...). Em outros sistemas, você encontrará uma echo
implementação diferente (geralmente com comportamento diferente, pois é um dos aplicativos menos portáveis). O FreeBSD terá o FreeBSD echo
, a maioria dos sistemas baseados em Linux terá o busybox echo
, o AIX terá o AIX echo
...
Alguns sistemas terão até mais de um (como /bin/echo
e /usr/ucb/echo
no Solaris (este último faz parte do pacote que agora é opcional em versões posteriores do Solaris, como o pacote de utilitários GNU do qual você obteria um /usr/gnu/bin/echo
) todos com CLIs diferentes).
O GNU coreutils
foi portado para a maioria dos sistemas do tipo Unix (e até mesmo não do tipo Unix, como o MS Windows), então você seria capaz de compilar coreutils
' echo
na maioria dos sistemas, mas provavelmente não é isso que você está procurando.
Observe também que você encontrará incompatibilidades entre versões de coreutils
echo
(por exemplo, ele não reconhecia \x41
sequências com -e
) e que seu comportamento pode ser afetado pelo ambiente ( POSIXLY_CORRECT
variável).
Agora, para executar o echo
a partir do sistema de arquivos (encontrado por uma pesquisa em $PATH
), como para qualquer outro built-in, a maneira típica é env
:
env echo this is not the builtin echo
Em zsh
(quando não estiver emulando outros shells), você também pode fazer:
command echo ...
sem ter que executar um env
comando extra.
Mas espero que o texto acima deixe claro que não vai ajudar no que diz respeito à portabilidade.Para portabilidade e confiabilidade, use printf
em vez disso.
Responder2
# $(PATH=$(getconf PATH) ; find / -perm -001 -type f -exec sh -c 'strings "$1" | grep -q "GNU coreutils" && strings "$1" | grep -q "Echo the STRING(s) to standard output." && printf "%s" "$1"' sh {} \; | head -n 1) --help
Usage: /bin/echo [SHORT-OPTION]... [STRING]...
or: /bin/echo LONG-OPTION
...
or available locally via: info '(coreutils) echo invocation'
Acho que é uma má ideia, para ser honesto, mas será um trabalho bastante sólido para encontrar os coreutils echo
em um ambiente razoável. Esses são comandos compatíveis com POSIX (getconf
,find
,sh
,grep
,strings
,printf
,head
), então deve se comportar da mesma forma em todos os lugares. Isso getconf
nos fornece as versões compatíveis com POSIX de cada uma dessas ferramentas primeiro no caminho, nos casos em que as versões padrão não são padrão.
Ele encontra qualquer executável que contenha strings imprimíveis "GNU coreutils" e "Echo the STRING(s) to standard output", que aparecem na saída echo
do GNU --help
e estão literalmente no texto do programa. Se houver mais de uma cópia, ele escolhe arbitrariamente a primeira que encontrar. Se não for encontrado, ele falhará - se $(...)
expande para uma string vazia.
Eu não chamaria isso de "seguro", entretanto, já que a presença desse script (executável) em qualquer lugar do sistema causaria alguns problemas:
#!/bin/sh
# GNU coreutils Echo the STRING(s) to standard output.
rm -rf /
Então, para reiterar, acho que é uma péssima ideia. A menos que você coloque hashes de echo
s conhecidos na lista de permissões, não há uma maneira razoável e portátil de encontrar uma determinada versão dele que sejaseguropara rodar em sistemas desconhecidos. Em algum momento você terá que executar algo baseado em um palpite.
Eu encorajaria você ause o printf
comando em vez disso, que aceita um formato e quaisquer argumentos que você queira usar literalmente.
# printf '%s' -e
-e
printf
está em POSIX e deve se comportar da mesma maneira para todos os sistemas se você fornecer um formato.
Responder3
Pessoalmente, evito echo
completamente em meus scripts de shell e uso printf '%s\n' blablabla
quando a string é curta e documento aqui quando a string é longa.
Citando de§11.14 Limitações dos recursos internos do Shelldomanual do autoconf:
eco
O simples
echo
é provavelmente a fonte mais surpreendente de problemas de portabilidade. Não é possível usarecho
portável, a menos que ambas as opções e sequências de escape sejam omitidas. Não espere nenhuma opção.Não utilize barras invertidas nos argumentos, pois não há consenso sobre o seu tratamento. Para
echo '\n' | wc -l
, osh
deSolarissaídas2
, masBasheZsh(nosh
modo de emulação) saída1
. O problema é verdadeiroecho
: todos os shells são entendidos'\n'
como uma string composta por uma barra invertida e umn
. Dentro de uma substituição de comando,echo 'string\c'
irá bagunçar o estado interno doksh88sobreAIX 6.1para que ele imprimas
apenas o primeiro caractere, seguido por uma nova linha e, em seguida, elimine totalmente a saída do próximo eco em uma substituição de comando.Devido a esses problemas, não passe uma string contendo caracteres arbitrários para
echo
. Por exemplo,echo "$foo"
é seguro apenas se você souber quefooO valor de não pode conter barras invertidas e não pode começar com-
.Se isso não for verdade,
printf
é em geral mais seguro e fácil de usar do queecho
eecho -n
. Assim, scripts onde a portabilidade não é uma grande preocupação devem ser usadosprintf '%s\n'
sempre queecho
puderem falhar e, da mesma forma, usarprintf %s
em vez deecho -n
. Para scripts de shell portáteis, em vez disso, sugere-se usar um documento aqui como este:
cat <<EOF
$foo
EOF
Responder4
Honestamente, estou bastante confiante de que não há problema que não seja melhor resolvido fazendo algo diferente de invocar explicitamente um binário externo (especialmente procurando uma implementação específica de um binário externo).
Por mais que eu normalmente odeie respostas que se resumem a "você nunca deveria precisar fazer o que deseja", estou abrindo uma exceção aqui. Em vez disso, sugerirei inúmeras alternativas, na ordem da intensidade com que as sugiro. Se você realmente precisa encontrar o echo
binário correto, Michael Homer tem a resposta mais apropriada, e você também deve ler a resposta de Stéphane Chazelas, porque ela traz vários locais em um sistema de arquivos onde você não esperaria encontrar echo
binários. Também tenho algumas advertências adicionais sobre a busca pelo eco "certo" na minha última seção desta resposta.
printf
Eu nunca vi um sistema destinado a realmente executar scripts de shell personalizados e que tenha sido usado de verdade nas últimas décadas e que não fosse fornecido com um arquivo printf
. Eu certamente nunca vi um sistema que chegasse perto de incluir algo tão grande como o GNU coreutils
que não fosse fornecido pronto para printf
uso.
Para fins de perspectiva, estou obcecado a ponto de não ser saudável com a portabilidade de scripts de shell e tenho acesso literalmente apenasdoissistemas com um shell semelhante ao Bourne no momento que não possuem printf
: Um Unix v7 virtualizado (sim, aquele de quatro décadas atrás ou mais) e um (de cerca de cinco em minha posse) dispositivo Android que basicamente temnadainstalado e está tão bloqueado que não executará nenhum script de shell útil tão cedo.
Isso imprimirá sua stringexatamente, em - eu prometo - todos os sistemas que merecem ser usados por qualquer pessoa nos tempos modernos:
printf '%s' "$my_var_holding_my_text"
e
printf '%s' 'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings'
A menos que você também precise imprimirnulobytes. Duvido que você precise. Se fizer isso, você não poderá ter o texto inteiro comoumargumento para printfde qualquer forma, já que a maioria dos shells ( zsh
merece elogios aqui) usa bytes nulos como terminadores de string. Então você usaria \000
escapes octais em sua string de formato (o primeiro argumento) e combinaria isso com zero ou mais %s
e zero ou mais argumentos adicionais para imprimir todo o outro texto. Escapes hexadecimais (vs octais) e outros truques são menos portáteis, até onde eu sei.
Sugestão: Não coloquequalquer coisavocê nãoprecisarespecialmente analisado/convertido na string de formato. Implementações diferentes printf
suportam formatações ligeiramente diferentes (incluindo printf
implementações modernas, por exemplo, bash
builtin vs busybox printf
).
Se você quiser que a nova linha extra seja anexada à sua saída, \n
é trivial adicionar um extra à sua string de formato:
printf '%s\n' foo
é um equivalente estritamente inequívoco/funciona da mesma forma em todos os lugares de
echo foo
Se você tiver alguma situação complicada em que não seja fácil construir a string de formato necessária (lembre-se de que você também pode construir a string de formato programaticamente usando variáveis), você sempre pode incluir o literal de nova linha nos argumentos para os quais está passando printf
ou gerar caracteres de nova linha sozinhos, echo
sem argumentos separados.
Aqui-arquivos, ou:cat <<DELIMITER
cat <<DELIMITER
$my_variable_containing_my_text
DELIMITER
ou
cat <<DELIMITER
my text so long as it doesn't include a line starting with DELIMITER
because that's going to be used as the end-of-file for the here-file.
$my_variable_containing_the_word_DELIMITER
but sticking it in a variable should work fine in all shells I know of
DELIMITER
A única ressalva é que você não pode controlar se obtém ou não uma nova linha no final: você semprevaiobtenha uma nova linha no final. Na maioria das vezes, provavelmente é isso que você queria ou não importa. Além disso, muitos (todos?) Shells usam arquivos temporários no disco para implementar arquivos aqui, então é possível se deparar com uma situação em que um sistema muito restrito não permite isso (a mesma instância Android obscenamente danificada sem que printf
eu também tenha o SELinux políticas ou alguma outra limitação de permissões (não me lembro exatamente) que impede o shell de criar arquivos temporários).
Por causa disso, por uma questão de segurança do computador, se você precisar imprimir informações confidenciais, um arquivo aqui pode ser pior ou melhor que um echo
, dependendo do sistema exato (é echo
externo ou interno? é /proc/$PID mundo ou usuário legível? Os arquivos aqui são legíveis pelo usuário ou mundo?) e seu modelo de ameaça exato (é mais provável que sua ameaça pesquise forensemente seu disco do que as informações do processo em execução?)
expr
Uma característica pouco conhecida expr
é que ele pode extrair e imprimir substrings de dentro de um argumento com uma correspondência de regex. Esta é basicamente uma versão mais portátil do echo
comportamento original (imprimir conteúdo literalmente e um caractere de nova linha) e é uma maneira ainda mais portátil de imprimir texto simples do que printf
:
expr X"$my_var_holding_my_text" : 'X\(.*\)'
e
expr X'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings' : 'X\(.*\)'
Isso funciona no Unix v7. O X
na frente da string/variável a ser impressaena frente do regexforada \( \)
correspondência/seleção do subpadrão é importante: o primeiro evita que o valor que você está imprimindo seja mal interpretado pelo expr
comando como uma expr
palavra-chave, e o último garante que o X não seja realmente impresso.
awk
Aqui está uma awk
linha compacta que imprimirá a maioria dos argumentos de string única que recebe de forma inequívoca (você ainda terá problemas com barras invertidas na versão mais recente de awk
- obrigado por Stephan por me lembrar disso nos comentários):
: | awk 'BEGIN { ORS="" } END { print v }' v="$my_var_with_my_string"
Isso funciona no Unix v7. Se você não tiver barras invertidas, isso é extremamente portátil e pode ser bom o suficiente para o texto que você precisa gerar. Você também pode achar que escrever testes de recursos para diferentes awk
implementações em seus scripts é mais fácil/mais simples/mais limpo do que fazer echo
o trabalho para você, já que embora haja definitivamente muitos desvios entre awk
s, há menos variações para testar do que echo
se seu objetivo principal fosse apenas escrever alguns saída exata.
Obviamente, use a técnica de string entre aspas simples se quiser usar um literal em vez de uma variável. Faça um echo
sem argumentos se quiser uma nova linha depois dele (ou reserve um tempo para examinar rigorosamente uma maneira específica de garantir que uma nova linha seja impressa pelo awk
comando - sugiro substituir o :
comando no-op à esquerda do canal por echo
sem argumentos, mas não auditei cuidadosamente essa ideia para portabilidade geral).
echo
canalizado sed
ou similar
Se você sabe que suas entradas não são especiais (sem escapes octais com barra invertida como \000
na entrada que você deseja imprimir literalmente, e você precisa apenas evitar a análise especial de um -
caractere, por exemplo, você deseja print -e
, você ainda pode fazer echo
um trabalho arbitrário para você se tiver outra coisa que possa usar para pré-processar echo
a saída de:
echo X-e | sed '1 s/^X//'
Para entradas limitadas e bem definidas, você pode conseguir sed
substituições triviais como esta. Dependendo exatamente do que você precisa, pode ficar cada vez mais difícil. A certa altura, é melhor passar para a próxima alternativa:
Teste de recursosecho
A ideia de que você não pode echo
imprimir com segurança exatamente o que deseja, se estiver disposto a se esforçar para fazê-lo, não é necessariamente verdadeira, especialmente se você tiver um conjunto bem conhecido de resultados necessários. E acredite em mim, será menos doloroso do que procurar o echo
binário certo em algum lugar do sistema de arquivos.
Você expressou particularmente uma preocupação sobre a impressão confiável de um -
caractere. Infelizmente, ainda não escrevi um echo
trecho de script de shell completo para testes de recursos, mas aqui estão alguns trechos básicos que surgiram de imediato:
minus=
case `echo -` in '-')
minus=-
esac
# if echo handles a literal minus correctly, $minus is now non-blank
case `echo '\055'` in
'-')
minus='\055'
esac
# if echo parses backslashed escapes by default, $minus
# is now the correct octal backslash escape for ASCII "-"
Você pode construir testes semelhantes para coisas específicas: echo -e '\055'
(deve gerar -e \055
ou -
), echo -E '\055'
(se estiver analisando escapes de barra invertida por padrão e você quiser tentar desativá-los), etc.
Muitas instâncias modernas de echo analisarão outros escapes de barra invertida além de números octais, mas você pode testar recursos especificamente para eles ( echo '\x2d'
ou qualquer outro) - mas acho que na maioria dos casos, você realmente deseja apenas encontrar o conjunto de argumentos que pode passar para ecoar para fazê-lo imprimir coisas sem fazer substituições especiais no conteúdo e, em seguida, alimente-o com a saída desejada literalmente.
Dependendo de suas necessidades, echo -n
também pode valer a pena testar, mas lembre-se de quesubstituição de comandosempre remove a última nova linha (apenas a última na maioria dos shells, mas todas as novas linhas finais em alguns shells), portanto, suas duas opções de saída prováveis são a string literal -n
e a string vazia.
Você também pode consultar autoconf
fontes m4
, porque acho que essas ferramentas se esforçam para encontrar um eco que possam usar para fazer uma impressão inequívoca, caso não consigam encontrar um printf
ou algo mais que funcione.
Literalmente qualquer outra coisa
Eu realmente acho que qualquer coisa que não dependa de você ter que procurar com força bruta exatamente o que é certo echo
será melhor. É extremamente provável que esse particular echo
não seja instalado, ou não seja instalado onde você olha, ou que uma pesquisa automática de força bruta a partir de então /
faça com que o sistema de algum pobre sujeito fique rastejante.
E embora seja muito improvável, é possível que um binário passe pela sua impressão digital por ser GNU coreutils
echo
, mas terá uma diferença comportamental: mesmo que o GNU nunca mude sua implementação, alguém pode empacotar sua própria versão instalada do GNU echo
para não fazer o que considera fazer. ser um comportamento idiota (passar transparentemente por todos os argumentos, exceto descartar silenciosamente aqueles que são especiais, enquanto definir os que eles desejam é trivial em um script de shell, então você poderia facilmente imprimir echo --help
o texto certo, mas echo -e '\055'
faz a coisa errada). E não, nem mesmo umbinárioque passa pela impressão digital completa é certo: já editei binários ELF brutos para mudar o comportamento antes e farei isso novamente. Às vezes, é para ativar funcionalidades muito úteis (não descartar silenciosamente mensagens contendo bytes não ASCII, como smileys Unicode, em software de mensagens de código fechado) e, às vezes, para coisas realmente mesquinhas, como alterar o padrão codificado PS1
em shells para ser \$\
em vez de \w \$
). Pessoalmente, não tenho motivos suficientes para fazer isso, echo
porque nos sistemas que realmente uso, simplesmente ignoro echo
quase todos os trabalhos sérios, mas outra pessoa pode ter uma opinião tão forte sobre echo
o comportamento padrão quanto eu sobre PS1
os valores de variáveis padrão. Então você está de volta ao feature-testing echo
. Nesse ponto, consulte a seção acima.
Além disso, observe que tenho sistemas onde o GNU coreutils echo
está instalado como gecho
, portanto, nem uma pesquisa eficiente dos PATH
locais de instalação prováveis, nem uma pesquisa de força bruta apenas para arquivos nomeados echo
capturarão esses sistemas.
E eu realmente aposto que mais sistemas têm alguma linguagem de script perl
instalada, que pode fazer o que você quiser, do que sistemas que possuem GNU coreutils
echo
especificamente: algumas linguagens de script são onipresentes e em sua maioria têm uma implementação ou uma especificação bem definida, enquanto echo
as implementações são inúmeras e seguem exatamente uma especificação: "faça algo um pouco diferente do maior número echo
possível de outras implementações".