¿Por qué debo poner el comando read en un subshell mientras uso la canalización [duplicar]
El comando df .
puede mostrarnos en qué dispositivo estamos. Por ejemplo,
me@ubuntu1804:~$ df .
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sdb1 61664044 8510340 49991644 15% /home
Ahora quiero conseguir la cuerda /dev/sdb1
.
Intenté así pero no funcionó: df . | read a; read a b; echo "$a"
este comando me dio una salida vacía. Pero df . | (read a; read a b; echo "$a")
funcionará como se esperaba.
Estoy un poco confundido ahora.
Sé que (read a; read a b; echo "$a")
es una subcapa, pero no sé por qué tengo que hacer una subcapa aquí. Según tengo entendido, x|y
redirigirá la salida de x
a la entrada de y
. ¿Por qué read a; read a b; echo $a
no puede obtener la entrada pero una subcapa sí?
Respuestas
El principal problema aquí es agrupar los comandos correctamente. Las subcapas son un problema secundario.
x|y
redirigirá la salida dex
a la entrada dey
Sí, pero x | y; z
no redirigirá la salida de x
ambos y
y z
.
En df . | read a; read a b; echo "$a"
, la canalización solo se conecta df .
y read a
los otros comandos no tienen conexión con esa canalización. Tienes que agrupar los read
s juntos: df . | { read a; read a b; }
o df . | (read a; read a b)
que la tubería se conecte a ambos.
Sin embargo, ahora viene el problema de la subcapa: los comandos en una canalización se ejecutan en una subcapa, por lo que establecer una variable en ellos no afecta al shell principal. Por lo tanto, el echo
comando debe estar en la misma subcapa que read
s. Por lo tanto: df . | { read a; read a b; echo "$a"; }
.
Ahora bien, si usa ( ... )
o { ...; }
no hace una diferencia particular aquí, ya que los comandos en una canalización se ejecutan en subcapas de todos modos.
Una alternativa es utilizar una sustitución de proceso :
{ read header; read filesystem rest; } < <(df .)
echo "$filesystem"
La <(...)
sustitución del proceso ejecuta el script contenido (en una subcapa), pero actúa como un nombre de archivo, por lo que necesita el primero <
para redirigir el contenido (que es la salida del script) al script reforzado. Los comandos agrupados se ejecutan en el shel actual ;.
Puede ser complicado conseguir que esto sea legible, pero puede poner cualquier espacio en blanco arbitrario entre las llaves y la sustitución del proceso.
{
read header
read filesystem rest
} < <(
df .
)
echo "$filesystem"
Y podría ser más fácil usar una herramienta externa para extraer el sistema de archivos:
filesystem=$( df . | awk 'NR == 2 {print $1}' )
Tu primer comando
df . | read a; read a b; echo "$a"
efectivamente se interpreta como
( df . | read a ) ; read a b; echo "$a"
Entonces, la canalización solo se alimenta del read a
comando.
Dado que desea varias lecturas de la canalización, debe agrupar los comandos.
Ahora no tiene por qué ser una subcapa; podría ser una agrupación ...
bash-4.2$ df | { read a ; read a b ; echo $a ; }
devtmpfs
Más comúnmente, es posible que desee un bucle
bash-4.2$ df | while read a > do > read a b > echo $a
> done
devtmpfs
tmpfs
/dev/vda3
/dev/vdb
Hay un problema secundario con bash
el lado derecho de una canalización que se ejecuta en una subcapa, por lo que los $a
$b
valores no son accesibles fuera del while
ciclo, ¡pero ese es un problema diferente!