为啥 sudo cat 给予 Permission denied 但 sudo vim 工作正常? [复制]
Posted
技术标签:
【中文标题】为啥 sudo cat 给予 Permission denied 但 sudo vim 工作正常? [复制]【英文标题】:Why sudo cat gives a Permission denied but sudo vim works fine? [duplicate]为什么 sudo cat 给予 Permission denied 但 sudo vim 工作正常? [复制] 【发布时间】:2012-04-25 11:13:23 【问题描述】:我正在尝试在我的 arch 的 pacman.conf 文件中自动添加存储库源,但在我的 shell 脚本中使用 echo
命令。但是,它会像这样失败:-
sudo echo "[archlinuxfr]" >> /etc/pacman.conf
sudo echo "Server = http://repo.archlinux.fr/\$arch" >> /etc/pacman.conf
sudo echo " " >> /etc/pacman.conf
-bash: /etc/pacman.conf: Permission denied
如果我使用 vim 手动更改 /etc/pacman.conf,通过这样做
sudo vim /etc/pacman.conf
并使用:wq
退出 vim,一切正常,我的 pacman.conf 已手动更新,没有“Permission denied”投诉。
为什么会这样?我如何让sudo echo
工作? (顺便说一句,我也尝试过使用sudo cat
,但也因权限被拒绝而失败)
【问题讨论】:
【参考方案1】:正如@geekosaur 解释的那样,shell 在运行命令之前会进行重定向。当您输入以下内容时:
sudo foo >/some/file
你当前的 shell 进程会创建一个自身的副本,它首先尝试打开 /some/file
进行写入,然后如果成功,它将将该文件描述符作为标准输出,并且只有成功后它才会执行 sudo
。第一步就失败了。
如果您被允许(sudoer 配置通常会阻止运行 shell),您可以执行以下操作:
sudo bash -c 'foo >/some/file'
但我发现一个好的解决方案通常是使用| sudo tee
而不是>
和| sudo tee -a
而不是>>
。如果重定向是我首先需要sudo
的唯一原因,这将特别有用;毕竟,以 root 身份不必要地运行进程正是 sudo
的创建目的。以 root 身份运行 echo
实在是太愚蠢了。
echo '[archlinuxfr]' | sudo tee -a /etc/pacman.conf >/dev/null
echo 'Server = http://repo.archlinux.fr/$arch' | sudo tee -a /etc/pacman.conf >/dev/null
echo ' ' | sudo tee -a /etc/pacman.conf >/dev/null
我在末尾添加了> /dev/null
,因为tee
将它的输出发送到 命名文件和 它自己的标准输出,我不需要在我的终端上看到它。 (tee
命令就像物理管道中的“T”连接器,这就是它的名字。)我切换到单引号('
...'
)而不是双引号(@987654338 @..."
) 所以一切都是字面意思,我不必在$arch
中的$
前面加上反斜杠。 (如果没有引号或反斜杠,$arch
将被 shell 参数 arch
的值替换,这可能不存在,在这种情况下,$arch
将被任何内容替换并消失。)
这样就可以使用sudo
以root 身份写入文件。现在对在 shell 脚本中输出包含换行符的文本的方法进行了冗长的题外话。 :)
正如他们所说,为了 BLUF 它,我首选的解决方案是将此处的文档输入到上述 sudo tee
命令中;那么根本不需要cat
或echo
或printf
或任何其他命令。单引号已经移到了前哨引言<<'EOF'
,但它们在那里的效果是一样的:正文被视为文字文本,所以$arch
被单独留下:
sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/$arch
EOF
虽然我会这样做,但还有其他选择。以下是一些:
您可以坚持每行使用一个echo
,但将它们全部组合在一个子shell中,因此您只需附加到文件一次:
(echo '[archlinuxfr]'
echo 'Server = http://repo.archlinux.fr/$arch'
echo ' ') | sudo tee -a /etc/pacman.conf >/dev/null
如果您将-e
添加到echo
(并且您使用的是支持该非POSIX 扩展的shell),您可以使用\n
将换行符直接嵌入到字符串中:
# NON-POSIX - NOT RECOMMENDED
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' |
sudo tee -a /etc/pacman.conf >/dev/null
但正如上面所说,这不是 POSIX 指定的行为;你的shell可能只是回显一个文字-e
,然后是一个带有一堆文字\n
s的字符串。 POSIX 的做法是使用printf
而不是echo
;它会像echo -e
那样自动处理它的参数,但不会在末尾自动添加换行符,因此您也必须在此处添加一个额外的\n
:
printf '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n \n' |
sudo tee -a /etc/pacman.conf >/dev/null
对于这两种解决方案,命令作为参数字符串得到的内容包含两个字符序列\n
,这取决于命令程序本身(printf
或 echo
中的代码)来翻译它进入换行符。在许多现代 shell 中,您可以选择使用 ANSI 引号 $'
...'
,这将在命令程序看到字符串之前将 \n
之类的序列转换为 literal 换行符。这意味着此类字符串适用于任何命令,包括普通的旧 -e
-less echo
:
echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' |
sudo tee -a /etc/pacman.conf >/dev/null
但是,虽然比 echo -e
更便携,但 ANSI 引号仍然是非 POSIX 扩展。
再一次,虽然这些都是选项,但我更喜欢上面的直接tee <<EOF
解决方案。
【讨论】:
有趣!您能否添加一条注释来解释同时将文件分块到 /dev/null 的原因?sudo bash -c 'foo >/some/file'
在 osx 上为我工作 :-)
我更喜欢这个答案,因为它适用于多行文本。 cat <<EOF | sudo tee -a /some/file > /dev/null ...
这实际上是我的最后一个答案,除了你添加了一个无关的cat
进程。 :)【参考方案2】:
问题在于重定向是由您的原始 shell 处理的,而不是由 sudo
处理的。 Shell 无法读心,也不知道特定的>>
是为sudo
而设计的,而不是为它设计的。
你需要:
-
引用重定向(所以它被传递给
sudo)
和使用sudo -s
(以便sudo
使用shell 来处理引用的重定向。)
【讨论】:
所以像这样分两步进行 (1)sudo -s
(2) echo "# test" >> /etc/pacman.conf 有效。但是有可能在一行中执行吗?
sudo -s 'echo "# test" >>/etc/pacman.conf'
是我想传达给你的。
实际上,在阅读了您上面的答案后,我尝试过这样做,但遇到了一个奇怪的错误,如下所示:- sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directory
这就是为什么我随后尝试了两步手动过程。
啊.....以这种方式进行的最后一次尝试成功了-echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
我如何/在哪里可以找到有关 sudo
配置禁用 -s
的信息?视觉?【参考方案3】:
http://www.innovationsts.com/blog/?p=2758
由于上面的说明不是很清楚,我正在使用该博客文章中的说明。通过示例可以更轻松地了解您需要做什么。
$ sudo cat /root/example.txt | gzip > /root/example.gz -bash: /root/example.gz: 权限被拒绝
请注意,导致错误的是管道中的第二个命令(gzip 命令)。这就是我们使用带有 -c 选项的 bash 技术的用武之地。
$ sudo bash -c 'cat /root/example.txt | gzip > /root/example.gz' $ sudo ls /root/example.gz /root/example.gz
我们可以从 ls 命令的输出中看到压缩文件创建成功。
第二种方法与第一种方法相似,因为我们将命令字符串传递给 bash,但我们是通过 sudo 在管道中进行的。
$ sudo rm /root/example.gz $ echo "cat /root/example.txt | gzip > /root/example.gz" |须藤 bash $ sudo ls /root/example.gz /root/example.gz
【讨论】:
对于这样一个简单的命令,使用 sh 而不是 bash 可能会更好一些。例如。 sudo sh -c 'cat /root/example.txt | gzip > /root/example.gz'。但除此之外,很好的解释,+1。【参考方案4】:sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'
【讨论】:
【参考方案5】:步骤 1 在 bash 文件中创建函数 (write_pacman.sh
)
#!/bin/bash
function write_pacman
tee -a /etc/pacman.conf > /dev/null << 'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/\$arch
EOF
'EOF'
不会解释 $arch
变量。
STE2 源 bash 文件
$ source write_pacman.sh
第三步执行函数
$ write_pacman
【讨论】:
EOF 上的报价是什么?【参考方案6】:附加文件(sudo cat):
cat <origin-file> | sudo tee -a <target-file>
将回显附加到文件(sudo echo):
echo <origin> | sudo tee -a <target-file>
(EXTRA) 忽略输出:
echo >origin> | sudo tee -a <target-file> >/dev/null
【讨论】:
以上是关于为啥 sudo cat 给予 Permission denied 但 sudo vim 工作正常? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
为啥linux有时候命令会提示 Permission denied
为啥linux显示自己的权限不够???我加了sudo了,看下面。。。
Mac下使用sudo提示permission denied的解决方法