Por que o Shell Substituição de Comando consome um caractere de nova linha à direita?

25

Conforme o exemplo a seguir e como na minha pergunta recente No bash, para onde foi o último caractere da nova linha? , Eu quero saber "por que" isso acontece

  x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p 
# Output is: 610a62 
# The trailing newline from the 'echo' command
#   has been "deleted" by Command Substitution

Eu suponho que deve haver alguma razão muito significativa para uma ação do shell, ou seja, Substituição de Comando, para realmente excluir alguns dados da saída de comando que está substituindo ...
mas não consigo entender esse, como parece ser a antítese do que é suposto fazer .. ie. passar a saída de um comando de volta ao processo de script ... Reter um caractere me parece estranho, mas suponho que exista uma razão sensata para isso ... Estou ansioso para descobrir qual é esse motivo. .

Peter.O
fonte

Respostas:

22

Porque o shell não foi originalmente projetado para ser uma linguagem de programação completa.

É bastante difícil remover uma trilha \nde alguma saída de comando. No entanto, para fins de exibição, quase todos os comandos terminam sua saída com \n, então ... deve haver uma maneira simples de removê-lo quando você deseja usá-lo em outro comando. A remoção automática com a $()construção foi a solução escolhida.

Então, talvez você aceite esta pergunta como resposta:

Você pode encontrar uma maneira simples de remover a trilha \nse isso não tiver sido feito automaticamente no seguinte comando?

> echo The current date is "$(date)", have a good day!

Observe que é necessário citar para impedir o esmagamento de espaços duplos que podem aparecer em datas formatadas.

Stéphane Gimenez
fonte
11
Se o que você diz é verdade, eu ficaria absolutamente atordoado. Se houver uma shoptalteração no comportamento padrão descrito, eu ficaria feliz novamente ... Acho difícil pensar que o Bash / Linux / Unix poluiria uma função tão crítica como "capturar stdout" apenas porque datenão possui uma opção with / without '\ n' ... A correção óbvia seria que o bash (ou outro shell) possui um shoptpara isso ... Você conhece alguma? .. e você tem algum link de referência que fale dos porquês e dos motivos desta questão? ... e por que não datetem uma opção \ n .. Deveria!
Peter.O
2
As substituições de comandos apareceram na primeira versão do shell Bourne na "7ª edição" do Unix em 1979. Já era uma grande revolução e eu acho (eu não nasci) que ninguém se importava em seguir '\ n' ou não. Sim, você pode ler a página de manual em menos de 10 minutos e parece que nada mudou sobre a substituição de comandos até então. Exceto pela $()sintaxe alternativa preferida .
Stéphane Gimenez
Obrigado .. Eu pensei que isso poderia ser um problema de legado, e certamente está se moldando ao fato de que é melhor eu começar a assistir minhas Substituições de Comando com mais cuidado. Até hoje eu devo ter sobrevivido em uma ala e uma oração. Alguns desses "acontecimentos misteriosos" que encontrei estão começando a fazer sentido agora :) ... Então, no caso inverso do seu "encontro" positivo, se Eu quero manter a minha fuga \ n, devo código algo como isto :( { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }.. assim seja :)
Peter.O
PS é meu comentário acima: Claro, só dateiria funcionar, mas eu simplesmente usado datecomo um exemplo de qualquer comando ..
Peter.O
Sua 'pergunta' sobre a data foi a mais forte na minha compreensão de 'por que' a Substituição de Comando faz o que faz, então essa foi a 'melhor' resposta para minha pergunta ... e eu certamente também precisava das outras boas respostas ..
Peter.O
19

Faz parte do padrão :

O shell deve expandir a substituição do comando executando o comando em um ambiente de subcamação (consulte Ambiente de Execução do Shell ) e substituindo a substituição do comando (o texto do comando mais o "$ ()" ou as aspas posteriores) pela saída padrão do comando, removendo seqüências de um ou mais <newline> s no final da substituição.

Obviamente, o padrão provavelmente é escrito dessa maneira, porque foi assim kshque aconteceu ou algo assim, mas é o padrão e é um comportamento documentado. Se você não gostar, use Perl ou outra coisa que permita manter as novas linhas finais.

Steven Pritchard
fonte
Um bom resumo .. Obrigado .. e os links certamente ajudaram
Peter.O 31/07/11
4
O padrão é assim porque é assim que o Bourne fez isso e todos os outros desde então.
Gilles 'SO- stop be evil'
6

Bem, isso faz sentido para mim. A nova linha está lá apenas em primeiro lugar, na saída normal do comando, para que o prompt apareça após a conclusão do comando em uma nova linha. A nova linha não faz parte da saída original na maioria dos casos, está lá para arrumar a tela. Quando você está analisando a saída de um comando, a nova linha no final geralmente é problemática. Por que owc comando gera 2 linhas de texto? Ah, não, ele gera um seguido por uma nova linha. Quando você analisa wc, não quer se preocupar com o fato de haver duas linhas de saída - não há realmente, apenas uma.

EightBitTony
fonte
@ EightBitTony: Minha pergunta não está relacionada à exibição de algo no terminal. Isso é bastante direto. Se houver uma nova linha para exibir, ela exibirá a nova linha. O ponto em questão aqui é que a \nno stdoutfluxo original é removida pela Substituição de Comando. ie meus dados originais (dados muito importantes) têm novas linhas à direita removidas, mesmo quando os dados são agrupados em "aspas". Entendo razoavelmente como e por que o Word Splitting funciona, mas não faço a menor idéia do porquê da Substituição de Comando remover apenas novas linhas e apenas as linhas finais .
Peter.O
11
Nada do que você disse muda minha visão. Mais frequentemente, a nova linha final em qualquer saída existe apenas para fins de exibição, não como parte dos dados.
EightBitTony 31/07
Concordo com a sua opinião e tenho o tempo todo ... Sim, a nova linha no final da saída é por conveniência ... mas meu problema não tem nada a ver com isso ... Estou perguntando: Por que a Substituição por Comando a remove à força ? ... Essas são duas questões diferentes. Talvez minha pergunta deva ser: Existe uma solução alternativa embutida? . porque ter que codificar esse problema adicionando um char fictício à direita, é uma merda! ... Eu farei isso se for necessário, mas esta é a primeira vez que sou impedida pelo bash (e estou em estado de choque :) .. Faz muito bem ...
Peter.O 31/07
Como mencionado em outra resposta, faz parte do padrão. Para mim, acho que é assim porque, quando você pós-processa os dados, deseja apenas vê-los, e não a nova linha conveniente. É porque o shell espera que você esteja manipulando os dados em um pipe, e a nova linha não é dados. Mais e precisamos levar isso para uma discussão.
EightBitTony 31/07
Obrigado .. Eu já entendi muito bem a idéia agora .. Parece ser um caso de Substituição de Comando simplesmente inclinar-se a abandonar as novas linhas finais, em vez de mantê-las. Deve haver alguma sabedoria nesse movimento que eu não senti " "ainda, mas eu achava que o uso popular esmagador de todas as letras maiúsculas de minúsculas nos nomes de arquivos era um pouco estranho / desnecessário, mas eu estava pensando outro dia que é realmente um sistema bastante confortável. ver (mais profundamente) a rima ea razão do comportamento do comando de substituição, algum dia ...
Peter.O
1

Eu estava com o mesmo problema e encontrei o exemplo abaixo. Parece que citar ajudou a situação, pelo menos para mim:

http://tldp.org/LDP/abs/html/commandsub.html .

dir_listing=`ls -l`
echo $dir_listing     # unquoted

# Expecting a nicely ordered directory listing.

# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# The newlines disappeared.


echo "$dir_listing"   # quoted
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh
christian_hercules
fonte
2
Aqui está o que está acontecendo aqui. Digamos que você tenha uma variável $str, contendo a string "a b c". Se você passar $strpara echosem aspas ( echo $str), echoreceberá a, be ccomo três argumentos distintos. Seu shell não se importa com o espaço em branco entre os argumentos. echoimprimirá esses argumentos com espaços que separam cada um, para que você entenda "a b c". Se você passar a variável echoentre aspas ( echo "$str"), o shell a vê como um argumento e a echorecebe como tal, para que o espaço em branco não seja recolhido. O mesmo se aplica às novas linhas.
11
O problema que o pôster original está tendo é que as novas linhas à direita (apenas as últimas) de seus comandos estão desaparecendo. A menos que passe a -nbandeira, echoadiciona uma nova linha à direita à saída, para que ambos os ecos no seu exemplo tenham novas linhas à direita. No entanto, se você tentar echo -n $dir_listingou echo -n "$dir_listing", verá que nenhuma nova linha à direita é impressa com ou sem aspas $dir_listing, o que sugere que o problema está na substituição do comando.
2
TLDP é um mal lugar desatualizado para aprender sobre shell script. Experimente o Wiki Wooledge Bash. mywiki.wooledge.org/BashGuide
curinga
-1

por que echo "hello "(sem as aspas) devora o espaço? o valor dos caracteres IFS é visto pelo shell como sendo delimitadores; portanto, os caracteres finais (que não delimitam nada) estão sendo removidos. A substituição de recomendação é simplesmente executar as recomendações em um sub-shell, portanto, segue a mesma lógica.

Philomath
fonte
@ Philomath: Ambos x="hello  "e x="hello     "perderão todos os seus espaços finais se eles forem exibidos via echo $x... No entanto, apenas a última nova linha do Substituton de Comando será perdida.
Peter.O
estranho, eu não vejo qualquer diferença no meu bash (verificação com xxd)
Philomath
Eu apenas checke isso ... Ele remove todas as novas linhas finais ... Eu estava experimentando o IFS quando tive essa impressão, então vamos cancelar essa :) .. mas a questão ainda permanece ... É uma parte fundamental repartição de palavras para condensar vários espaços para um único espaço, e para remover totalmente esquerda e à direita espaço em branco .. Eu posso entender isso, mas eu certamente não entendo por que, com comando de substituição, é única tiras \ n e não todos os espaços (como com a divisão de palavras), e por que apenas o final à direita? .. e acima de tudo: "Substituição de comando" substitui apenas parcialmente. Por que?
Peter.O
4
IFSnão tem nada a ver com remover novas linhas no final das substituições de comando.
Gilles 'SO- stop be evil'