我想将整个标准输入读入变量。我不想产生新的进程。
从 bash 手册:
命令替换
$(cat file)
可以替换为等效但更快的$(< file)
。
它主要有效,但这些/proc/fd
东西很容易被以下内容破坏sudo
:
# same user. works
[root@okdistr ~]# echo aaa | bash -c 'echo $(</dev/stdin)'
aaa
# different user. fail
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(</dev/stdin)'
bash: /dev/stdin: Permission denied
# spawn new process. not want
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(cat)'
aaa
# try to read from fd: silently fails
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(<&0)'
# works, but too complex
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'a=; while true; do rc=0; read -N1024 b || rc=$?; a=$a$b; [ $rc = 0 ] || break; done; echo "$a"'
aaa
[root@okdistr ~]#
Run Code Online (Sandbox Code Playgroud)
为什么会$(<&0)
失败?
首先,我建议的解决方案如下所示。此后,将讨论您观察到的两个错误。
Bash 变量不能包含 NUL 字符。因此,如果文件不包含此类字符,则只能将整个文件读入 bash 变量。受此 bash 限制的影响,以下内容应该适合您:
$ echo aaa | { read -rd "" v; echo "$v"; }
aaa
Run Code Online (Sandbox Code Playgroud)
使用read -d '' var
,bash 将读取标准输入,直到找到 NUL 字符。由于 bash 变量无论如何都不能包含 NUL 字符,因此这种方法不会限制您超越 bash 的固有限制。
-r
阻止read
bash 解释反斜杠序列的选项。另外,如果您想保留前导和尾随空格,请在语句IFS=
之前添加read
。
# echo aaa | sudo -u nobody bash -c 'echo $(<&0)'
#
Run Code Online (Sandbox Code Playgroud)
即使没有 sudo,也会发生上述静默失败:
$ echo aaa | bash -c 'echo $(<&0)'
$
Run Code Online (Sandbox Code Playgroud)
它甚至在没有创建子 shell 的情况下发生:
$ echo aaa | echo $(<&0)
$
Run Code Online (Sandbox Code Playgroud)
问题是这&0
不是有效的文件名。观察:
$ echo aaa | cat &0
[1] 22635
bash: 0: command not found
Run Code Online (Sandbox Code Playgroud)
在 bash 中,&0
只有与<
或组合时才有意义>
。
我们再看一下 bash 文档:
命令替换 $(cat file) 可以替换为等效但更快的 $(< file)。
既然cat &0
不起作用,人们也不应该指望$(< &0)
它能起作用。
这似乎是一件合理的事情:
# echo aaa | sudo -u nobody bash -c 'echo $(cat /dev/stdin)'
cat: /dev/stdin: Permission denied
Run Code Online (Sandbox Code Playgroud)
要了解失败的原因,让我们检查以下权限/dev/stdin
:
# echo aaa | sudo -u nobody bash -c 'ls -lH /dev/stdin'
prw------- 1 root root 0 Jul 1 15:42 /dev/stdin
Run Code Online (Sandbox Code Playgroud)
用户没有权限访问该文件。这是合理的:人们不希望任何人乱搞 root 的文件。
“更智能”的操作系统可能知道/dev/stdin
在这种特殊情况下没有人可以访问,但是出于安全目的,操作系统不尝试超越自己可能是件好事。