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

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

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

来自 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

...到标准输出...

相关内容