为啥 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 命令中;那么根本不需要catechoprintf 或任何其他命令。单引号已经移到了前哨引言<<'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,然后是一个带有一堆文字\ns的字符串。 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,这取决于命令程序本身(printfecho 中的代码)来翻译它进入换行符。在许多现代 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 &lt;&lt;EOF 解决方案。

【讨论】:

有趣!您能否添加一条注释来解释同时将文件分块到 /dev/null 的原因? sudo bash -c 'foo &gt;/some/file' 在 osx 上为我工作 :-) 我更喜欢这个答案,因为它适用于多行文本。 cat &lt;&lt;EOF | sudo tee -a /some/file &gt; /dev/null ... 这实际上是我的最后一个答案,除了你添加了一个无关的cat 进程。 :)【参考方案2】:

问题在于重定向是由您的原始 shell 处理的,而不是由 sudo 处理的。 Shell 无法读心,也不知道特定的&gt;&gt; 是为sudo 而设计的,而不是为它设计的。

你需要:

    引用重定向(所以它被传递给sudo) 使用sudo -s(以便sudo 使用shell 来处理引用的重定向。)

【讨论】:

所以像这样分两步进行 (1) sudo -s (2) echo "# test" >> /etc/pacman.conf 有效。但是有可能在一行中执行吗? sudo -s 'echo "# test" &gt;&gt;/etc/pacman.conf' 是我想传达给你的。 实际上,在阅读了您上面的答案后,我尝试过这样做,但遇到了一个奇怪的错误,如下所示:- sudo -s 'echo "# test" &gt;&gt; /etc/pacman.conf' /bin/bash: echo "# test" &gt;&gt; /etc/pacman.conf: No such file or directory 这就是为什么我随后尝试了两步手动过程。 啊.....以这种方式进行的最后一次尝试成功了-echo 'echo "# test" &gt;&gt; /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的解决方法

转:ubuntu添加用户adduser,并给予sudo权限

ubuntu /usr/bin/sudo: Permission denied

ubuntu用户添加adduser, useradd并给予sudo权限