95

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

4

6 回答 6

141

正如@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”连接器,这就是它的名字。)我切换到单引号('... ')而不是双引号( "... ),"所以一切都是字面的,我不必在 in 前面加上$反斜杠$arch。(如果没有引号或反斜杠,$arch将被 shell 参数的值替换,该值arch可能不存在,在这种情况下,$arch将被任何内容替换并消失。)

这样就可以使用sudo. 现在对在 shell 脚本中输出包含换行符的文本的方法进行了冗长的题外话。:)

正如他们所说,为了 BLUF 它,我首选的解决方案是将此处的文档输入到上述sudo tee命令中;那么根本不需要cator echoor printfor 任何其他命令。单引号已移至前哨引言<<'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

如果您添加-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,并且由命令程序本身(内部的代码printfor echo)将其转换为换行符。在许多现代 shell 中,您可以选择使用 ANSI 引号$'... ,这将在命令程序看到字符串之前'将序列\n转换为文字换行符。这意味着此类字符串可以与任何命令一起使用,包括普通的旧-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上面的直接解决方案。

于 2012-04-13T16:57:22.277 回答
58

问题是重定向正在由您的原始 shell 处理,而不是由sudo. 贝壳不能读心,也不知道那个特定的东西>>是为了sudo而不是为了它。

你需要:

  1. 引用重定向(所以它被传递给sudo)
  2. 使用sudo -s(以便sudo使用 shell 来处理引用的重定向。)
于 2012-04-13T03:40:18.867 回答
18

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

于 2012-06-13T13:57:00.857 回答
7
sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'
于 2017-01-23T19:26:12.433 回答
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
于 2014-04-03T17:16:42.490 回答
0

附加文件(sudo cat):

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

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

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

(额外)忽略输出:

echo >origin> | sudo tee -a <target-file> >/dev/null
于 2020-10-07T19:00:44.150 回答