该命令df .
可以显示我们所在的设备。例如,
me@ubuntu1804:~$ df .
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sdb1 61664044 8510340 49991644 15% /home
现在我想获取字符串/dev/sdb1
。
我尝试这样做,但它不起作用:df . | read a; read a b; echo "$a"
,这个命令给了我一个空的输出。但df . | (read a; read a b; echo "$a")
会按预期工作。
我现在有点困惑。
我知道这(read a; read a b; echo "$a")
是一个子shell,但我不知道为什么我必须在这里制作一个子shell。据我了解,x|y
会将 的输出重定向x
到 的输入y
。为什么read a; read a b; echo $a
无法获取输入但子 shell 可以?
答案1
这里的主要问题是正确地对命令进行分组。子shell 是次要问题。
x|y
将重定向输出x
到输入y
是的,但x | y; z
不会将 的输出重定向x
到y
和z
。
在 中df . | read a; read a b; echo "$a"
,管道仅连接df .
和read a
,其他命令与该管道没有连接。您必须将read
它们组合在一起:df . | { read a; read a b; }
或者df . | (read a; read a b)
将管道连接到它们。
但是,现在出现了子 shell 问题:管道中的命令在子 shell 中运行,因此在其中设置变量不会影响父 shell。因此该echo
命令必须与read
s 位于同一子 shell 中。所以:df . | { read a; read a b; echo "$a"; }
。
现在,无论您使用( ... )
还是{ ...; }
使用,都没有什么特别的区别,因为管道中的命令无论如何都在子 shell 中运行。
答案2
另一种方法是使用流程替代:
{ read header; read filesystem rest; } < <(df .)
echo "$filesystem"
进程<(...)
替换执行包含的脚本(在子 shell 中),但它的作用类似于文件名,因此您需要首先<
将内容(即脚本的输出)重定向到大括号脚本中。分组的命令在当前的shell中执行;。
使其可读可能很困难,但您可以将任意空格放入大括号和过程替换中。
{
read header
read filesystem rest
} < <(
df .
)
echo "$filesystem"
使用外部工具提取文件系统可能会更容易:
filesystem=$( df . | awk 'NR == 2 {print $1}' )
答案3
你的第一个命令
df . | read a; read a b; echo "$a"
实际上被解释为
( df . | read a ) ; read a b; echo "$a"
所以管道只输入read a
命令。
由于您希望从管道中进行多次读取,因此您需要将命令分组在一起。
现在它不必是一个子shell;而是一个子shell。可以是一个分组..
bash-4.2$ df | { read a ; read a b ; echo $a ; }
devtmpfs
更常见的是,您可能需要一个循环
bash-4.2$ df | while read a
> do
> read a b
> echo $a
> done
devtmpfs
tmpfs
/dev/vda3
/dev/vdb
还有一个次要问题是,bash
管道的右侧正在运行子 shell,因此$a
$b
无法在while
循环外部访问这些值,但这是一个不同的问题!