Basicamente, é um problema de portabilidade (e confiabilidade).
Inicialmente, echo
não aceitava nenhuma opção e não expandia nada. Tudo o que estava fazendo era apresentar seus argumentos separados por um caractere de espaço e finalizados por um caractere de nova linha.
Agora, alguém pensou que seria bom se pudéssemos fazer coisas como echo "\n\t"
gerar nova linha ou caracteres de tabulação ou ter uma opção para não gerar o caractere de nova linha à direita.
Eles então pensaram mais, mas em vez de adicionar essa funcionalidade ao shell (como perl
onde aspas duplas, \t
na verdade significa um caractere de tabulação), eles o adicionaram echo
.
David Korn percebeu o erro e introduziu uma nova forma de citações de shell: $'...'
que depois foi copiada bash
e zsh
que era muito tarde para isso.
Agora, quando um UNIX padrão echo
recebe um argumento que contém os dois caracteres \
e t
, em vez de produzi-los, gera um caractere de tabulação. E assim que aparece \c
em um argumento, ele para de produzir (portanto, a nova linha à direita também não é produzida).
Outros shells / fornecedores / versões do Unix escolheram fazer de maneira diferente: eles adicionaram uma -e
opção para expandir as seqüências de escape e uma -n
opção para não gerar a nova linha final. Alguns têm -E
que desativar sequências de escape, outros têm, -n
mas -e
a lista de seqüências de escape suportadas por uma echo
implementação não é necessariamente a mesma que a suportada por outra.
Sven Mascheck tem uma boa página que mostra a extensão do problema .
Pelos echo
implementações que suportam opções, há geralmente nenhum apoio de um --
para marcar o fim de opções (o echo
builtin de algumas conchas não-Bourne-como fazer, e zsh suporta -
para que embora), assim, por exemplo, é difícil para a saída "-n"
com echo
no muitas conchas.
Em alguns shells como bash
¹ ou ksh93
² ou yash
( $ECHO_STYLE
variável), o comportamento depende até de como o shell foi compilado ou do ambiente ( echo
o comportamento do GNU também mudará se $POSIXLY_CORRECT
estiver no ambiente e com a versão 4 , zsh
com sua bsd_echo
opção, alguns baseados em pdksh com sua posix
opção ou se são chamados como sh
ou não). Portanto, dois bash
echo
s, mesmo da mesma versão do, bash
não garantem o mesmo comportamento.
O POSIX diz: se o primeiro argumento é -n
ou qualquer argumento contém barras invertidas, o comportamento não é especificado . bash
O eco nesse sentido não é POSIX, pois, por exemplo, echo -e
não está sendo emitido -e<newline>
conforme o POSIX exige. A especificação UNIX é mais rígida, proíbe -n
e exige a expansão de algumas seqüências de escape, incluindo a \c
que interrompe a saída.
Essas especificações não são realmente úteis aqui, pois muitas implementações não são compatíveis. Mesmo alguns sistemas certificados como o macOS 5 não são compatíveis.
Para realmente representar a realidade atual, o POSIX deve realmente dizer : se o primeiro argumento corresponder ao ^-([eEn]*|-help|-version)$
regexp estendido ou se algum argumento contiver barras invertidas (ou caracteres cuja codificação contenha a codificação do caractere de barra invertida, como α
nos códigos de idioma usando o conjunto de caracteres BIG5), o comportamento será não especificado.
Em suma, você não sabe o echo "$var"
que produzirá, a menos que possa ter certeza de que $var
não contém caracteres de barra invertida e não começa com -
. A especificação POSIX realmente nos diz para usar printf
nesse caso.
Então, o que isso significa é que você não pode usar echo
para exibir dados não controlados. Em outras palavras, se você estiver escrevendo um script e estiver recebendo entradas externas (do usuário como argumentos ou nomes de arquivos do sistema de arquivos ...), não poderá usá echo
-lo para exibi-lo.
Isso é bom:
echo >&2 Invalid file.
Isso não é:
echo >&2 "Invalid file: $file"
(Embora funcione bem com algumas echo
implementações (não compatíveis com UNIX), como bash
as quando a xpg_echo
opção não foi ativada de uma maneira ou de outra como no momento da compilação ou através do ambiente).
file=$(echo "$var" | tr ' ' _)
não é OK na maioria das implementações (excepções são yash
com ECHO_STYLE=raw
(com a ressalva de que yash
's variáveis não pode conter seqüências arbitrárias de bytes por isso não nomes de arquivos arbitrários) e zsh
' s echo -E - "$var"
6 ).
printf
, por outro lado, é mais confiável, pelo menos quando está limitado ao uso básico de echo
.
printf '%s\n' "$var"
Produzirá o conteúdo de $var
seguido por um caractere de nova linha, independentemente de qual caractere ele possa conter.
printf '%s' "$var"
A saída será sem o caractere de nova linha à direita.
Agora, também existem diferenças entre printf
implementações. Há um núcleo de recursos especificado pelo POSIX, mas há muitas extensões. Por exemplo, alguns suportam a %q
para citar os argumentos, mas como isso é feito varia de shell para shell, algum suporte \uxxxx
para caracteres unicode. O comportamento varia printf '%10s\n' "$var"
em locais de vários bytes, existem pelo menos três resultados diferentes paraprintf %b '\123'
Mas, no final, se você se ater ao conjunto de recursos do POSIX printf
e não tentar fazer algo muito sofisticado com ele, estará sem problemas.
Mas lembre-se de que o primeiro argumento é o formato, portanto, não deve conter dados variáveis / não controlados.
Um mais confiável echo
pode ser implementado usando printf
, como:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
O subshell (que implica gerar um processo extra na maioria das implementações de shell) pode ser evitado usando-se local IFS
com muitos shells ou escrevendo-o como:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Notas
1. como bash
o echo
comportamento pode ser alterado.
Com bash
, no tempo de execução, há duas coisas que controlam o comportamento de echo
(ao lado enable -n echo
ou redefinindo echo
como uma função ou alias): a xpg_echo
bash
opção e se bash
está no modo posix. posix
O modo pode ser ativado se bash
for chamado como sh
ou se POSIXLY_CORRECT
estiver no ambiente ou com a posix
opção:
Comportamento padrão na maioria dos sistemas:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo
expande seqüências conforme o UNIX exige:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Ainda honra -n
e -e
(e -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
Com xpg_echo
e modo POSIX:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-n A
Desta vez, bash
é compatível com POSIX e UNIX. Observe que, no modo POSIX, bash
ainda não é compatível com POSIX, pois não é produzido -e
em:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
Os valores padrão para xpg_echo e POSIX pode ser definida em tempo de compilação com os --enable-xpg-echo-default
e --enable-strict-posix-default
opções para o configure
script. Isso é tipicamente o que as versões recentes do OS / X fazem para criar as suas /bin/sh
. No Unix / Linux implementação / distribuição no seu perfeito juízo normalmente faria isso por /bin/bash
embora . Na verdade, isso não é verdade, o /bin/bash
que o Oracle envia com o Solaris 11 (em um pacote opcional) parece ter sido construído --enable-xpg-echo-default
(não era o caso no Solaris 10).
2. Como ksh93
o echo
comportamento pode ser alterado.
Em ksh93
, se echo
expande seqüências de escape ou não e reconhece opções depende do conteúdo das variáveis de ambiente $PATH
e / ou $_AST_FEATURES
.
Se $PATH
contiver um componente que contenha /5bin
ou /xpg
antes do componente /bin
ou /usr/bin
, ele se comportará da maneira SysV / UNIX (expande seqüências, não aceita opções). Se encontrar /ucb
ou /bsd
primeiro ou se $_AST_FEATURES
7 contiver UNIVERSE = ucb
, ele se comportará da maneira BSD 3 ( -e
para ativar a expansão, reconhece -n
).
O padrão é dependente do sistema, BSD no Debian (veja a saída de builtin getconf; getconf UNIVERSE
nas versões recentes do ksh93):
$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD
3. BSD para eco -e?
A referência ao BSD para o manuseio da -e
opção é um pouco enganosa aqui. A maioria desses echo
comportamentos diferentes e incompatíveis foram todos introduzidos na AT&T:
\n
, \0ooo
, \c
No Banco do programador Trabalho UNIX (baseado em Unix V6), eo restante ( \b
, \r
...) em Unix System III Ref .
-n
no Unix V7 (por Dennis Ritchie Ref )
-e
no Unix V8 (por Dennis Ritchie Ref )
-E
possivelmente veio inicialmente bash
(CWRU / CWRU.chlog na versão 1.13.5 menciona Brian Fox adicionando-o em 18/10/1992, GNU echo
copiando-o logo depois no sh-utils-1.8 lançado 10 dias depois)
Embora o echo
built-in do sh
ou BSDs tenha suportado -e
desde o dia em que começaram a usar o shell Almquist no início dos anos 90, o echo
utilitário independente até hoje não o suporta por lá (o FreeBSDecho
ainda não suporta -e
, embora suporte -n
como Unix V7 (e também \c
mas apenas no final do último argumento)).
A manipulação de -e
foi adicionada a ksh93
s echo
no universo BSD na versão ksh93r lançada em 2006 e pode ser desativada no momento da compilação.
4. Mudança de comportamento do GNU eco em 8.31
Desde coreutils 8.31 (e este cometem ), GNU echo
agora expande seqüências de escape por padrão quando POSIXLY_CORRECT está no ambiente, para coincidir com o comportamento de bash -o posix -O xpg_echo
's echo
incorporado (ver relatório de erro ).
5. macOS echo
A maioria das versões do macOS recebeu certificação UNIX do OpenGroup .
Seu sh
embutido echo
é compatível, pois é bash
(uma versão muito antiga) criada com xpg_echo
ativado por padrão, mas seu echo
utilitário independente não é. env echo -n
não produz nada em vez de -n<newline>
, env echo '\n'
gera em \n<newline>
vez de <newline><newline>
.
Essa /bin/echo
é a do FreeBSD que suprime a saída de nova linha se o primeiro argumento for -n
ou (desde 1995) se o último argumento terminar \c
, mas não suporta nenhuma outra sequência de barra invertida exigida pelo UNIX, nem mesmo \\
.
6. echo
implementações que podem gerar dados arbitrários literalmente
Estritamente falando, você também pode contar que o FreeBSD / macOS /bin/echo
acima (não o shell deles echo
), onde zsh
s echo -E - "$var"
ou yash
's ECHO_STYLE=raw echo "$var"
( printf '%s\n' "$var"
) podem ser escritos:
/bin/echo "$var
\c"
As implementações suportadas -E
e -n
(ou podem ser configuradas para) também podem:
echo -nE "$var
"
E zsh
's echo -nE - "$var"
( printf %s "$var"
) poderia ser escrito
/bin/echo "$var\c"
7. _AST_FEATURES
e o ASTUNIVERSE
Ele _AST_FEATURES
não deve ser manipulado diretamente, é usado para propagar as definições de configuração AST pela execução do comando. A configuração deve ser feita através da astgetconf()
API (não documentada) . Por dentro ksh93
, o getconf
built-in (ativado com builtin getconf
ou chamando command /opt/ast/bin/getconf
) é a interface paraastgetconf()
Por exemplo, você faria builtin getconf; getconf UNIVERSE = att
para alterar a UNIVERSE
configuração para att
(causando o echo
comportamento do SysV entre outras coisas). Depois de fazer isso, você notará que a $_AST_FEATURES
variável de ambiente contém UNIVERSE = att
.
echo -e
?