我想将整个标准输入读入变量中。我不想产生新的进程。
来自 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 ~]#
为什么会$(<&0)
失败?
答案1
首先,我建议的解决方案如下所示。此后,将讨论您观察到的两个错误。
建议的解决方案
Bash 变量不能包含 NUL 字符。因此,如果文件不包含此类字符,则只能将整个文件读入 bash 变量。受此 bash 限制的影响,以下内容应该适合您:
$ echo aaa | { read -rd "" v; echo "$v"; }
aaa
使用read -d '' var
,bash 将读取标准输入,直到找到 NUL 字符。由于 bash 变量无论如何都不能包含 NUL 字符,因此这种方法不会限制您超越 bash 的固有限制。
阻止 bash 解释反斜杠序列的-r
选项。read
另外,如果您想保留前导和尾随空格,请在语句IFS=
之前添加read
。
“尝试从 fd 读取:默默失败”
# echo aaa | sudo -u nobody bash -c 'echo $(<&0)'
#
即使没有 sudo,也会发生上述静默失败:
$ echo aaa | bash -c 'echo $(<&0)'
$
它甚至在没有创建子 shell 的情况下发生:
$ echo aaa | echo $(<&0)
$
问题是这&0
不是有效的文件名。观察:
$ echo aaa | cat &0
[1] 22635
bash: 0: command not found
在 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
要了解失败的原因,让我们检查以下权限/dev/stdin
:
# echo aaa | sudo -u nobody bash -c 'ls -lH /dev/stdin'
prw------- 1 root root 0 Jul 1 15:42 /dev/stdin
用户没有权限访问该文件。这是合理的:人们不希望任何人乱搞 root 的文件。
“更智能”的操作系统可能知道没有人可以访问/dev/stdin
在这种特殊情况下但是,出于安全目的,操作系统最好不要试图超越自己。
答案2
您不需要这些/dev
链接来执行此操作。我知道sudo
会清除环境变量,但您仍然可以以参数的形式传递变量,因为无论如何您都在调用另一个 shell...
sudo -u nobody bash -c '
a=$1; echo "$a"
' -- "$(echo aaa)"
...或者...
echo aaa | {
sudo -u nobody bash -c '
a=$1; echo "$a"
' -- "$(</dev/fd/0)"
}
这两个打印:
aaa
...到标准输出...