为什么sudo cat给出了权限被拒绝但sudo vim工作正常?

时间:2012-04-13 03:37:14

标签: bash shell echo cat

我正在尝试在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,但失败的权限也被拒绝了)

6 个答案:

答案 0 :(得分:110)

正如@geekosaur解释的那样,shell在运行命令之前执行重定向。当你输入这个:

sudo foo >/some/file

您当前的shell进程会创建自己的副本,首先尝试打开/some/file进行写入,然后将该文件描述符作为标准输出,然后才执行sudo

如果你被允许(sudoer configs经常阻止运行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”连接器,它就是它的名字。)我切换到单引号(' ... ')而不是双打(" ... "),以便所有内容都是文字的,我不必在$中的$arch前放置反斜杠。

这样就可以使用sudo以root身份写入文件。现在对在shell脚本中输出包含换行符的文本的方法进行冗长的讨论。 :)

首先,您可以在子shell中将所有echo组合在一起,因此您只需要进行一次重定向:

(echo '[archlinuxfr]
 echo 'Server = http://repo.archlinux.fr/$arch'
 echo ' ') | sudo tee -a /etc/pacman.conf >/dev/null

或使用printf代替echo,因此您可以使用\n将换行符直接嵌入到字符串中:

printf '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

bash中,您可以使用echo -e获得相同的结果:

# BASH ONLY - NOT RECOMMENDED
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

但是大多数shell只会在您尝试时输出-e,因此不建议这样做。

对于printfecho -e,命令作为参数字符串获取的内容包含一个文字反斜杠,后跟一个文字N,无论您键入\n,它都取决于命令程序本身(printfecho内的代码)将其转换为换行符。在许多现代shell中,您可以选择使用ANSI引号$' ... ',它会在命令之前将\n之类的序列转换为 literal 换行符程序永远看到字符串,这意味着这些字符串可以使用任何命令:

echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

但是,虽然比echo -e更便携,但ANSI引号仍然是非POSIX扩展名。

我这样做的首选方法是使用here-document,完全避免echoprintf

sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/$arch

EOF

答案 1 :(得分:42)

问题是原始shell正在处理重定向,而不是sudo。贝壳不能阅读思想,也不知道那个特定的>>是针对sudo而不是针对它的。{1}}。

你需要:

  1. 引用重定向(因此将其传递给sudo)
  2. 使用sudo -s(以便sudo使用shell来处理引用的重定向。)

答案 2 :(得分:17)

http://www.innovationsts.com/blog/?p=2758

由于上面的说明不明确,我使用的是博客文章中的说明。通过示例,您可以更轻松地查看需要执行的操作。

  

$ sudo cat /root/example.txt | gzip&gt; /root/example.gz
  -bash:/root/example.gz:权限被拒绝

请注意,这是导致错误的管道中的第二个命令(gzip命令)。这就是我们使用带有-c选项的bash的技术。

  

$ sudo bash -c'cat /root/example.txt | gzip&gt; /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&gt; /root/example.gz”| sudo bash
  $ sudo ls /root/example.gz
   /root/example.gz

答案 3 :(得分:5)

sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'

答案 4 :(得分:1)

第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

第3步执行功能

$ write_pacman

答案 5 :(得分:0)

附加文件(sudo cat):

cat <origin-file> | sudo tee -a <target-file>

将回声追加到文件(sudo回声):

echo <origin> | sudo tee -a <target-file>

(EXTRA)忽略输出:

echo >origin> | sudo tee -a <target-file> >/dev/null