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


86

我正在尝试在我的arch的pacman.conf文件中自动添加存储库源,但是要echo在shell脚本中使用该命令。但是,它失败是这样的:

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

并使用退出vim :wq,一切正常,我的pacman.conf已手动更新,没有“权限被拒绝”的投诉。

为什么会这样呢?我该如何sudo echo上班?(顺便说一句,我也尝试过使用,sudo cat但由于权限被拒绝也失败了)


Answers:


51

问题在于重定向是由您的原始Shell处理的,而不是sudo。外壳程序不具备读心术的能力,并且不知道该特定>>内容是为sudo而不是为它而设计的。

你需要:

  1. 引用重定向(因此将其传递给 sudo)
  2. 使用sudo -s(因此sudo使用外壳程序来处理引用的重定向。)

1
因此,分两个步骤执行(1)sudo -s(2)echo“#test” >> /etc/pacman.conf即可。但是是否可以在一行中执行此操作?
Calvin Cheng

15
sudo -s 'echo "# test" >>/etc/pacman.conf'是我想传达给您的。
geekosaur 2012年

2
实际上,在阅读完上述答案后,我尝试了此操作,但出现了如下奇怪的错误:-sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directory这就是为什么我随后尝试了两步手动操作的原因。
Calvin Cheng

16
啊.....以这种方式进行的最后一次尝试奏效了–echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
程凯文(Calvin Cheng)

1
我如何/在哪里找到sudo禁用配置的信息-s?维苏多?
Calvin Cheng

127

正如@geekosaur解释的那样,shell在运行命令之前会进行重定向。当您键入以下内容时:

sudo foo >/some/file

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

如果允许的话(sudoer配置通常会阻止运行shell),您可以执行以下操作:

sudo bash -c 'foo >/some/file'

但是我发现一个好的解决方案通常是使用| sudo tee代替>| sudo tee -a代替>>。如果重定向是我sudo首先需要的唯一原因,则这特别有用。毕竟,正是sudo要避免的根本原因是作为根运行不必要的进程。而echo以root身份运行只是愚蠢的。

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。(如果没有引号或反斜杠,$arch则将替换为shell参数的值,该参数arch可能不存在,在这种情况下,该$arch值将被替换为空,而消失。)

这样就可以使用写入根文件sudo。现在要花很多篇幅讨论如何在Shell脚本中输出包含换行符的文本。:)

就像他们说的那样,对BLUF来说,我的首选解决方案是将here-document馈入上述sudo tee命令;那么就没有必要catechoprintf或在所有其他任何命令。单引号已移至前哨简介<<'EOF',但在此处具有相同的效果:正文被视为原义文本,因此$arch被单独保留:

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

EOF

但是,尽管这是我的方式,但还有其他选择。这里有一些:

您可以坚持echo每行一个,但是将所有它们分组在一个子外壳中,因此您只需将其追加到文件一次:

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

如果添加-eecho(并且正在使用支持该非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,这取决于命令程序本身(printf或中的代码echo)将其转换为换行符。在许多现代的Shell中,您可以选择使用ANSI引号$'... ',它将在命令程序看到字符串之前将序列\n转换为原义的换行符。这意味着这些字符串可以与任何命令一起使用,包括普通的old -e-less echo

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

但是,尽管echo -eANSI引号比更具可移植性,但它仍然是非POSIX扩展名。

再说一次,尽管这些都是选择,但我更喜欢tee <<EOF上面的直接解决方案。


有趣!您能否添加注释以说明将文件也分块到/ dev / null的原因?
knite

sudo bash -c 'foo >/some/file'在osx上为我工作:-)
kayochin

我喜欢这个答案,因为它适用于多行文字。cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan

这实际上是我的最后答案,除了您添加了一个无关的cat过程。:)
马克·里德

17

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” | sudo bash
$ sudo ls /root/example.gz
/root/example.gz


1
对于这样一个简单的命令,使用sh代替bash可能会更好。例如sudo sh -c'cat /root/example.txt | gzip> /root/example.gz”。但是否则,很好的解释是+1。
布莱斯2012年


1

STEP 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

STEP 3执行功能

$ write_pacman

EOF的报价是什么?
bilabila

0

附加文件(须藤猫):

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

将echo附加到文件(sudo echo):

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

(EXTRA)忽略输出:

echo >origin> | sudo tee -a <target-file> >/dev/null
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.