升级到新的发行版本后,我的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/stdout
and /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`