¿Cuál es la diferencia entre <<
, <<<
y < <
en bash?
¿Cuál es la diferencia entre <<
, <<<
y < <
en bash?
Respuestas:
Aquí documenta
<<
que se conoce como here-document
estructura. Dejas que el programa sepa cuál será el texto final, y cada vez que se ve ese delimitador, el programa leerá todo lo que le has dado al programa como entrada y realizará una tarea sobre él.
Esto es lo que quiero decir:
$ wc << EOF
> one two three
> four five
> EOF
2 5 24
En este ejemplo, le decimos al wc
programa que espere la EOF
cadena, luego escriba cinco palabras y luego escriba EOF
para indicar que hemos terminado de dar entrada. En efecto, es similar a correrwc
solo, escribir palabras y luego presionarCtrlD
En bash, estos se implementan a través de archivos temporales, generalmente en forma /tmp/sh-thd.<random string>
, mientras que en el tablero se implementan como canalizaciones anónimas. Esto se puede observar a través de las llamadas del sistema de seguimiento con strace
comando. Reemplace bash
con sh
para ver cómo se /bin/sh
realiza esta redirección.
$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'
Aquí cadena
<<<
que se conoce como here-string
. En lugar de escribir texto, le da una cadena de texto prefabricada a un programa. Por ejemplo, con el programa bc
que podemos hacer bc <<< 5*4
para obtener resultados para ese caso específico, no es necesario ejecutar bc de forma interactiva.
Las cadenas aquí en bash se implementan a través de archivos temporales, generalmente en el formato /tmp/sh-thd.<random string>
, que luego se desvinculan, lo que hace que ocupen temporalmente un espacio de memoria pero no se muestren en la lista de /tmp
entradas de directorio, y efectivamente existan como archivos anónimos, que aún pueden se hará referencia a través del descriptor de archivo por el propio shell, y el descriptor de archivo será heredado por el comando y luego duplicado en el descriptor de archivo 0 (stdin) a través de la dup2()
función. Esto se puede observar a través de
$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)
lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd
Y a través de rastreo de llamadas al sistema (salida acortada para facilitar la lectura; observe cómo se abre el archivo temporal como FD 3, los datos grabados en ellos, entonces se vuelve a abrir con O_RDONLY
marcar como fd 4 y posteriores no enlazado, a continuación, dup2()
en fd 0, que se hereda por la cat
tarde ):
$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'
execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0
...
strace: Process 10229 attached
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid 10229] write(3, "TEST", 4) = 4
[pid 10229] write(3, "\n", 1) = 1
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4
[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0
[pid 10229] dup2(4, 0) = 0
[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0
...
[pid 10229] read(0, "TEST\n", 131072) = 5
[pid 10229] write(1, "TEST\n", 5TEST
) = 5
[pid 10229] read(0, "", 131072) = 0
[pid 10229] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
Opinión: potencialmente porque las cadenas aquí utilizan archivos de texto temporales, es la posible razón por la cual las cadenas aquí siempre insertan una nueva línea final, ya que el archivo de texto por definición POSIX debe tener líneas que terminen en un carácter de nueva línea.
Proceso de sustitución
Como tldp.org explica ,
La sustitución de procesos alimenta la salida de un proceso (o procesos) en el stdin de otro proceso.
En efecto, esto es similar a la canalización estándar de un comando al otro, por ejemplo echo foobar barfoo | wc
. Pero aviso: en la página de manual de bash tenga verá que se denota como <(list)
. Entonces, básicamente, puede redirigir la salida de múltiples (!) Comandos.
Nota: técnicamente cuando dice < <
que no se refiere a una cosa, sino a dos redirecciones con una sola <
redirección de salida y de proceso <( . . .)
.
¿Qué sucede si solo procesamos la sustitución?
$ echo <(echo bar)
/dev/fd/63
Como puede ver, el shell crea un descriptor de archivo temporal /dev/fd/63
donde va la salida (que según la respuesta de Gilles , es una tubería anónima). Eso significa <
que redirige ese descriptor de archivo como entrada en un comando.
Entonces, un ejemplo muy simple sería hacer la sustitución del proceso de salida de dos comandos echo en wc:
$ wc < <(echo bar;echo foo)
2 2 8
Así que aquí hacemos que Shell cree un descriptor de archivo para toda la salida que ocurre en el paréntesis y lo redirige como entrada a wc
. Como se esperaba, wc recibe esa secuencia de dos comandos de eco, que por sí solo generaría dos líneas, cada una con una palabra, y apropiadamente tenemos 2 palabras, 2 líneas y 6 caracteres más dos líneas nuevas contadas.
Nota al margen : La sustitución del proceso puede denominarse bashism (un comando o estructura que se puede usar en shells avanzados bash
, pero no especificado por POSIX), pero se implementó ksh
antes de la existencia de bash como la página de manual de ksh y esta respuesta sugiere. Conchas como tcsh
y mksh
sin embargo no tienen sustitución proceso. Entonces, ¿cómo podríamos redirigir la salida de múltiples comandos a otro comando sin sustitución del proceso? Agrupación más tuberías!
$ (echo foo;echo bar) | wc
2 2 8
Efectivamente, esto es lo mismo que el ejemplo anterior. Sin embargo, esto es diferente bajo el capó de la sustitución del proceso, ya que hacemos stdout de todo el subshell y stdin de wc
enlazado con la tubería . Por otro lado, la sustitución del proceso hace que un comando lea un descriptor de archivo temporal.
Entonces, si podemos agrupar con tuberías, ¿por qué necesitamos la sustitución de procesos? Porque a veces no podemos usar tuberías. Considere el siguiente ejemplo: comparar las salidas de dos comandos con diff
(que necesita dos archivos, y en este caso le damos dos descriptores de archivo)
diff <(ls /bin) <(ls /usr/bin)
< <
se usa cuando uno está recibiendo stdin de una sustitución de proceso . Tal orden podría ser: cmd1 < <(cmd2)
. Por ejemplo,wc < <(date)
< <
no es una cosa en sí misma, en el caso de la sustitución del proceso es solo <
seguido de otra cosa con la que comienza<
<<<
primero fue implementado por el puerto Unix de Plan 9 rc shell y luego adoptado por zsh, bash y ksh93. Entonces no lo llamaría bashism.
echo 'foo' | read; echo ${REPLY}
será no regresar foo
, porque read
se inicia en una sub-shell - tuberías inicia un sub-shell. Sin embargo, read < <(echo 'foo'); echo ${REPLY}
regresa correctamente foo
, porque no hay sub-shell.
< <
es un error de sintaxis:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
es la sustitución del proceso ( <()
) combinada con la redirección (<
):
Un ejemplo artificial:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
Con la sustitución del proceso, la ruta al descriptor de archivo se usa como un nombre de archivo. En caso de que no desee (o no pueda) usar un nombre de archivo directamente, combina la sustitución del proceso con la redirección.
Para ser claros, no hay < <
operador.
<()
le da un aspecto de nombre de archivo, por lo que es más útil en general: < <()
está reemplazando el stdin donde podría no ser necesario. En wc
, este último pasa a ser más útil. Podría ser menos útil en otros lugares
< <
es un error de sintaxis, probablemente quiere decir command1 < <( command2 )
que es una simple redirección de entrada seguida de una sustitución de proceso y es muy similar pero no equivalente a:
command2 | command1
La diferencia asumiendo que está ejecutando bash
se command1
ejecuta en un subshell en el segundo caso mientras se ejecuta en el shell actual en el primero. Eso significa que las variables establecidas command1
no se perderían con la variante de sustitución del proceso.
< <
dará un error de sintaxis. El uso adecuado es el siguiente:
Explicando con la ayuda de ejemplos:
Ejemplo para < <()
:
while read line;do
echo $line
done< <(ls)
En el ejemplo anterior, la entrada al bucle while vendrá del ls
comando que se puede leer línea por línea y echo
editar en el bucle.
<()
se usa para la sustitución de procesos. <()
Puede encontrar más información y ejemplos en este enlace: