bash: /dev/stderr: 权限被拒绝

bash: /dev/stderr: 权限被拒绝

升级到新的发行版本后,我的bash脚本开始出现错误:

bash: /dev/stderr: Permission denied

在以前的版本中 Bash 会内部认可这些文件名(这就是为什么这个问题不是重复的这个) 和做正确的事(tm)然而,这现在已经停止工作了。我该怎么做才能再次成功运行我的脚本?

我尝试将运行脚本的用户添加到组中tty,但这没有什么区别(即使在注销并重新登录后)。

我可以毫无问题地在命令行上重现此内容:

$ echo test > /dev/stdout
bash: /dev/stdout: Permission denied
$ echo test > /dev/stderr
bash: /dev/stderr: Permission denied
$ ls -l /dev/stdout /dev/stderr
lrwxrwxrwx 1 root root 15 May 13 02:04 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 May 13 02:04 /dev/stdout -> /proc/self/fd/1
$ ls -lL /dev/stdout /dev/stderr
crw--w---- 1 username tty 136, 1 May 13 05:01 /dev/stderr
crw--w---- 1 username tty 136, 1 May 13 05:01 /dev/stdout
$ echo $BASH_VERSION
4.2.24(1)-release

在旧系统(Ubuntu 10.04)上:

$ echo $BASH_VERSION
4.1.5(1)-release

答案1

我不认为这是完全bash问题。

在评论中,您说您在执行此操作后看到了此错误

sudo su username2

当登录为username.这就是su引发问题的原因。

/dev/stdout是 的符号链接/proc/self/fd/1,例如 的符号链接/dev/pts/1/dev/pts/1,它是一个伪终端,由 拥有且可写username;该所有权是在username登录时授予的。当您 时sudo su username2, 的所有权/dev/pts/1不会更改,并且username2没有写入权限。

我认为这是一个错误。 /dev/stdout 应该实际上,它是标准输出流的别名,但在这里我们看到了一种echo hello工作正常但echo hello > /dev/stdout失败的情况。

一种解决方法是成为username2组成员tty,但这将授予username2写入权限任何tty,这可能是不可取的。

另一种解决方法是登录帐户username2而不是使用su,以便/dev/stdout指向 拥有的新分配的伪终端username2。这可能不切实际。

另一种解决方法是修改您的脚本,使它们不引用/dev/stdoutand /dev/stderr;例如,替换此:

echo OUT > /dev/stdout
echo ERR > /dev/stderr

这样:

echo OUT
echo ERR 1>&2

我在我自己的系统 Ubuntu 12.04 和 bash 4.2.24 上看到了这一点——尽管info bash我系统上的 bash 文档 ( ) 这么说,/dev/stdout并且/dev/stderr在重定向中使用时会进行特殊处理。但即使 bash 没有特殊对待这些名称,它们仍然应该充当标准 I/O 流的等效名称。 (POSIX 没有提到/dev/std{in,out,err},所以很难说这是一个错误。)

查看旧版本的 bash,文档暗示/dev/stdout无论文件是否存在, et al 都会被特殊对待。该功能是在 bash 2.04 中引入的,NEWS该版本的文件显示:

重定向代码现在专门处理几个文件名:/dev/fd/N、/dev/stdin、/dev/stdout 和 /dev/stderr,无论它们是否存在于文件系统中。

但是如果您检查源代码 ( redir.c),您会发现启用了特殊处理仅有的是否定义了符号HAVE_DEV_STDIN(这是在从源代码构建 bash 时确定的)。

据我所知,bash 的发布版本已经对 et al 进行了无条件的特殊处理/dev/stdout——除非某些发行版已经修补了它。

所以另一个解决方法(我没有尝试过)是抓住bash 源,进行修改redir.c以使特殊/dev/*处理成为无条件的,并使用您重建的版本而不是系统附带的版本。不过,这可能有点矫枉过正了。

概括 :

您的操作系统和我的一样,没有正确处理/dev/stdout和的所有权和权限/dev/stderr。巴什据推测在重定向中特别对待这些名称,但实际上只有当文件不存在时才会这样做。如果/dev/stdout并且工作正常的话那并不重要/dev/stderr。仅当您su使用另一个帐户或执行类似操作时,此问题才会出现;如果您只是登录一个帐户,则权限是正确的。

答案2

因为我希望将相同的行转到控制台(或日志文件,如果重定向),用户将在其中逐字地看到它们,到一个过滤器,它会解析它们,我使用了somecommand | tee /dev/stderr | myfilter.

当用户尝试在sudo-ing...后运行我的脚本时,此问题就被打破了。

解决方法是检查是否/dev/stderr可写——如果

  • 没有sudo涉及到,或者
  • 如果标准错误被重定向到文件

所以这就是我所做的:

if [ -w /dev/stderr ]
then
        STDERR=/dev/stderr
else
        STDERR=/dev/tty # The only reason is that stderr is a link to console
fi
...
somecommand | tee $STDERR | myfilter

答案3

实际上,原因是 udev 在 tty 设备上专门将权限设置为 0620,而 su 不会也不应该更改所有权或权限。在我看来,这使我们陷入了 /dev/std* 不可移植的境地。

简单的解决方案是将“mesg y”放入 /etc/profile(或您喜欢使用的任何顶级配置文件)中,因为这会将您的 tty 设备的权限更改为 0622。我不太喜欢这样,但它可能更好而不是改变 udev 规则。

答案4

这并不理想,但要在问题发生后解决问题:

sudo chown -v $USER `tty`

相关内容