Para onde foi o último caractere de nova linha da minha substituição de comando?

15

O código a seguir descreve melhor a situação. Por que a última linha não está exibindo o caractere de nova linha à direita? A saída de cada linha é mostrada no comentário. Estou usando o GNU bash, versão 4.1.5

     echo -n $'a\nb\n'                  | xxd -p  # 610a620a  
           x=$'a\nb\n'   ; echo -n "$x" | xxd -p  # 610a620a
     echo -ne "a\nb\n"                  | xxd -p  # 610a620a
x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p  # 610a62
Peter.O
fonte
4
Solução alternativa para os raros momentos em que é necessário:tmp=$(somecommand; echo a); tmp=${tmp%a}
Gilles 'SO- stop be evil'
1
@Gilles: Re seu exemplo, acima: tmp=$(somecommand; echo a)... Isso certamente levou o ponto para casa ... Até eu ver o exemplo, minha tendência ainda seria usar echo -n a... mas, é claro !, não há necessidade de o -n, porque Comando Substituição irá remover o final de linha introduzida em qualquer caso! ... graças ...
Peter.O
stackoverflow.com/questions/613572/…
sancho.s Reinstala Monica 14/09

Respostas:

18

A função de substituição de comando $()(e seu primo, o backtick) remove especificamente as novas linhas finais. Esse é o comportamento documentado e você deve sempre estar ciente disso ao usar a construção.

As novas linhas dentro do corpo do texto não são removidas pelo operador de substituição, mas também podem ser removidas ao dividir palavras no shell, portanto, como isso depende depende se você usou aspas ou não. Observe a diferença entre esses dois usos:

$ echo -n "$(echo -n 'a\nb')"
a
b

$ !! | xxd -p
610a62

$ echo -n  $(echo -n 'a\nb')
a b

$ !! | xxd -p   
612062

No segundo exemplo, a saída não foi citada e a nova linha foi interpretada como uma divisão de palavras, fazendo com que ela apareça na saída como um espaço!

Caleb
fonte
1
Obrigado, Caleb. Eu estava ciente da divisão de palavras que mudava de espaço em branco para um único espaço quando não estava entre aspas ... É por isso que fiquei mais surpreso ao ver minha nova linha desaparecer, apesar de tê-la citado ... Agora estou ciente de que é por causa do comportamento 'normal' da Substituição de Comando soltar uma nova linha à direita ... Oh bem, c'est la vie .. e obrigado pelo link
Peter.O
6

Ao usar a substituição de comando, o shell executa os comandos em um subshell, retornando seu stdout. nesse processo, os caracteres do IFS perdem seu significado (se não estiverem entre aspas), pois o elogio retorna palavras simples e divididas, de modo que os caracteres finais são removidos. Por exemplo:

$ echo "$(echo -e '\n')" | wc
 1       0       1

$ echo -e '\n' | wc
2       0       2

e, mais praticamente, pwdfuncionará mesmo se o nome do diretório tiver uma nova linha no meio, mas$(pwd) não funcionará.

A solução comum é adicionar algo no final do seu comando e removê-lo posteriormente.

Philomath
fonte
1
Graças Philomath ... aliás, você primeiro exemplo é sintaticamente errado ( 'wc' não aceita uma string como um arg) .. Para você exemplos de fazer sentido, o primeiro poderia ser echo "$(echo -e '\n')" | wc, que gera 1   0   1, em comparação com o2   0   2
Peter.O
@ Fred: Opa, apenas um erro de digitação.
Philomath 31/07
1
"convertido em espaços" não é a explicação apropriada, há apenas algumas divisões envolvidas. Em particular, você pode remover o espaço do IFS e esses não funcionarão como separadores. Além disso, seu exemplo se enquadra em uma categoria de caso especial e há uma diferença entre $(pwd)e "$(pwd)", veja a resposta de Caleb.
Stéphane Gimenez
Sugestão PS..Your da solução usual é apenas o que eu precisava para limpar até um presente ..
Peter.O
Re "convertido em espaços", consulte Divisão de palavras
Peter.O