正如@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
命令;那么就没有必要cat
或echo
或printf
或在所有其他任何命令。单引号已移至前哨简介<<'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
如果添加-e
到echo
(并且正在使用支持该非POSIX扩展名的shell),则可以使用以下命令将换行符直接嵌入到字符串中\n
:
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
转换为原义的换行符。这意味着这些字符串可以与任何命令一起使用,包括普通的old -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
上面的直接解决方案。