¿Por qué Shell Command Substitution engulle un char de línea nueva?

25

Según el siguiente ejemplo, y como en mi pregunta reciente En bash, ¿a dónde se ha ido el char de Newline? , Quiero saber "por qué" sucede

  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

Supongo que debe haber una razón muy importante para que una acción de shell, a saber, la sustitución de comandos, realmente elimine algunos datos de la salida del comando que está sustituyendo ...
pero no puedo entender esto, ya que parece ser la antítesis de lo que se supone que debe hacer ... es decir. volver a pasar la salida de un comando al proceso del script ... Retener un personaje me parece extraño, pero supongo que hay una razón sensata para ello ... Estoy ansioso por descubrir cuál es esa razón ... .

Peter.O
fuente

Respuestas:

22

Porque el shell no estaba destinado originalmente a ser un lenguaje de programación completo.

Es bastante difícil eliminar un final \nde alguna salida de comando. Sin embargo, para fines de visualización, casi todos los comandos terminan con su salida \n, así que ... tiene que haber una forma simple de eliminarlo cuando desea usarlo en otro comando. La eliminación automática con la $()construcción fue la solución elegida.

Entonces, tal vez aceptes esta pregunta como respuesta:

¿Puedes encontrar una manera simple de eliminar el final \nsi esto no se hizo automáticamente en el siguiente comando?

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

Tenga en cuenta que se requieren comillas para evitar romper los espacios dobles que pueden aparecer en fechas formateadas.

Stéphane Gimenez
fuente
1
Si lo que dices es cierto, entonces estaría absolutamente atónito. Si hay shoptque cambiar su comportamiento predeterminado descrito, entonces estaría feliz de nuevo ... Me resulta difícil pensar que Bash / Linux / Unix contaminaría una función tan crítica como "capturar stdout" simplemente porque dateno tiene una opción con / sin '\ n' ... La solución obvia sería que bash (u otro shell) tiene un shoptpara esto ... ¿Conoces alguno? .. y ¿tiene algún enlace de referencia que hable de por qué y para qué se emite? ... y por qué no datetiene una opción \ n ... ¡Debería!
Peter.O
2
Las sustituciones de comandos aparecieron en la primera versión del shell Bourne en la "séptima edición" de Unix en 1979. Ya era una gran revolución y supongo (no nací) que a nadie le importaba seguir '\ n' o no. Sí, puede leer la página de manual en menos de 10 minutos, y parece que nada ha cambiado sobre la sustitución de comandos hasta entonces. Excepto por la $()sintaxis alternativa preferida .
Stéphane Gimenez
Gracias ... Pensé que este podría ser un problema heredado, y ciertamente se está configurando al hecho de que será mejor que comience a ver mis sustituciones de comandos con más cuidado. Hasta hoy debo haber estado sobreviviendo en un ala y una oración. Algunos de esos "sucesos misteriosos" que he encontrado están empezando a tener sentido ahora :) ... Entonces, en un caso inverso de su "cita" positiva, si Quiero mantener mi seguimiento \ n, debo codificar algo como esto :( { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }.. que así sea :)
Peter.O
PD: mi comentario anterior: Por supuesto, solo datefuncionaría, pero solo lo usé datecomo ejemplo de cualquier comando ...
Peter.O
Su 'pregunta' sobre la fecha fue el impulso más fuerte hacia mi comprensión de 'por qué' Command Substution hace lo que hace, por lo que esta ha sido la 'mejor' respuesta a mi pregunta ... y ciertamente también necesitaba las otras buenas respuestas. ..
Peter.O
19

Es parte del estándar :

El shell expandirá la sustitución del comando ejecutando el comando en un entorno de subshell (consulte Entorno de ejecución de Shell ) y reemplazando la sustitución del comando (el texto del comando más el "$ ()" o comillas anexas) con la salida estándar del comando, eliminando secuencias de uno o más <newline> s al final de la sustitución.

Por supuesto, el estándar probablemente está escrito de esta manera porque así es como lo kshhizo o algo así, pero es el estándar y es un comportamiento documentado. Si no le gusta, use Perl u otra cosa que le permita mantener las nuevas líneas finales.

Steven Pritchard
fuente
Un buen resumen ... Gracias ... y los enlaces ciertamente ayudaron
Peter.O
44
El estándar es así porque así es como lo hizo el shell Bourne y todos los demás desde entonces.
Gilles 'SO- deja de ser malvado'
6

Bueno, tiene sentido para mi. La nueva línea solo está allí en primer lugar, en la salida del comando normal, de modo que el mensaje aparece después de que el comando se completa en una nueva línea. La nueva línea no es parte de la salida original en la mayoría de los casos, está ahí para ordenar la pantalla. Cuando analiza el resultado de un comando, la nueva línea al final suele ser problemática. ¿Por qué el wccomando genera 2 líneas de texto? Oh, no lo hace, genera uno seguido de una nueva línea. Cuando analiza wc, no quiere preocuparse por el hecho de que hay 2 líneas de salida; en realidad no hay, solo hay una.

OchoBitTony
fuente
@EightBitTony: Mi pregunta no está relacionada de ninguna manera con mostrar algo en el terminal ... Eso es bastante sencillo. Si hay una nueva línea para mostrar, entonces mostrará la nueva línea. El punto en cuestión aquí es que \nla stdoutsustitución del comando elimina un en la secuencia original . es decir. mis datos originales (datos muy importantes) han eliminado las nuevas líneas finales, incluso cuando los datos están envueltos en "comillas". Entiendo razonablemente cómo y por qué funciona la división de palabras, pero no tengo ni idea de por qué la sustitución de comandos elimina solo las nuevas líneas y solo las finales .
Peter.O
1
Nada de lo que ha dicho cambia mi opinión. La mayoría de las veces, la nueva línea final en cualquier salida está allí solo para fines de visualización, no como parte de los datos.
EightBitTony
Estoy de acuerdo con su punto de vista, y tengo todo el tiempo ... Sí, la nueva línea al final de la salida es por conveniencia ... pero mi problema no tiene nada que ver con eso ... Estoy preguntando: ¿Por qué la sustitución de comandos lo elimina por la fuerza? ? ... Estos son dos problemas diferentes ... Tal vez mi pregunta debería ser: ¿Hay una solución integrada? . porque tener que codificar para este problema agregando un carácter simulado final, ¡apesta! ... Lo haré si tengo que hacerlo, pero esta es la primera vez que estoy bloqueado por bash (y estoy en estado de shock :) .. Lo hace muy bien ...
Peter.O
Como se mencionó en otra respuesta, es parte del estándar. Para mí, creo que lo hace de esa manera porque cuando postprocesas datos solo quieres ver los datos, no la nueva línea conveniente. Es porque el shell espera que manejes los datos en una tubería, y la nueva línea no son datos. Un poco más y tenemos que llevar esto a discusión.
EightBitTony
Gracias ... ahora tengo la idea bastante bien ... Parece ser un caso de Sustitución de Comando, simplemente se inclina hacia dejar caer las nuevas líneas finales, en lugar de mantenerlas ... Debe haber algo de sabiduría en ese movimiento que no he "sentido" "todavía, pero solía pensar que el uso abrumadoramente popular de todas las letras minúsculas en los nombres de archivo era un poco extraño / innecesario, pero estaba pensando el otro día que en realidad es un sistema bastante cómodo ... probablemente ver (más profundamente) la rima y la razón del comportamiento del comando de sustitución, algún día ...
Peter.O
1

Me encontraba con el mismo problema, y ​​encontré el siguiente ejemplo. Parece que la cita ayudó a la situación, al menos lo hizo para mí:

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
fuente
2
Esto es lo que está pasando aquí. Digamos que tiene una variable que $strcontiene la cadena "a b c". Si pasa $stra echosin comillas ( echo $str), echorecibirá a, by chasta tres argumentos separados. Su shell no se preocupa por el espacio en blanco entre los argumentos. echoluego imprimirá esos argumentos con espacios que separan cada uno, para que obtenga "a b c". Si pasa la variable echoentre comillas ( echo "$str"), el shell lo ve como un argumento y lo echorecibe como tal, por lo que el espacio en blanco no se contrae. Lo mismo se aplica a las nuevas líneas.
1
El problema que tiene el póster original es que las nuevas líneas finales (solo las últimas) de sus comandos están desapareciendo. A menos que pase la -nbandera, echoagrega una nueva línea final a su salida, por lo que ambos ecos en su ejemplo tienen nuevas líneas finales. Sin embargo, si intenta echo -n $dir_listingo echo -n "$dir_listing", verá que no se imprime una nueva línea final con o sin comillas $dir_listing, lo que sugiere que el problema está en la sustitución del comando.
2
tldp es un lugar muy desactualizado para aprender sobre los scripts de shell. Prueba el Wiki de Wooledge Bash en su lugar. mywiki.wooledge.org/BashGuide
Comodín el
-1

¿Por qué echo "hello "(sin las comillas) engulle el espacio? el shell ve el valor de los caracteres IFS como delimitadores, por lo que se eliminan los finales (que no delimitan nada). la sustitución de encomiendas es simplemente ejecutar los elogios en un subconjunto, por lo que sigue la misma lógica.

Philomath
fuente
@Philomath: Tanto x="hello  "y x="hello     "perderá todos sus espacios a la derecha si el se visualizan a través echo $x... Sin embargo , solamente la última línea nueva del Comando Substituton se pierde.
Peter
extraño, no veo ninguna diferencia en mi golpe (verificación con xxd)
Philomath
Acabo de comprobar esto ... Elimina todas las nuevas líneas finales ... Estaba experimentando con IFS cuando tuve esa impresión, así que cancelemos esa :) ... pero la pregunta sigue siendo ... Es una parte fundamental la división de palabras para condensar múltiples espacios a un solo espacio, y para eliminar totalmente espacio inicial y final .. yo puedo entender que, sin duda, pero yo no entiendo por qué, con el comando de sustitución, que sólo se despoja \ n y no todos los espacios en blanco (como se con división de palabras), y ¿por qué solo el final final? ... y sobre todo: "Sustitución de comandos" solo sustituye parcialmente ... ¿Por qué?
Peter
44
IFSno tiene nada que ver con quitar nuevas líneas al final de las sustituciones de comandos.
Gilles 'SO- deja de ser malvado'