命令替换:/dev/stdin:权限被拒绝

bas*_*sin 5 bash sudo

我想将整个标准输入读入变量。我不想产生新的进程。

从 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)失败?

Joh*_*024 2

首先,我建议的解决方案如下所示。此后,将讨论您观察到的两个错误。

建议的解决方案

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阻止readbash 解释反斜杠序列的选项。另外,如果您想保留前导和尾随空格,请在语句IFS=之前添加read

“尝试从 fd 读取:默默失败”

# 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 在这种特殊情况下没有人可以访问,但是出于安全目的,操作系统不尝试超越自己可能是件好事。