正如@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然后回显带有一串文字\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上面的直接解决方案。