正如@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”连接器,这也是它得名的原因。) 我使用单引号 (
'
...
'
) 替换双引号 (
"
...
"
),这样所有内容都是字面量,我不必在
$arch
中加反斜杠。(如果没有引号或反斜杠,
$arch
将被 shell 参数
arch
的值替换,该参数可能不存在,此时
$arch
被替换为空并消失。)
所以,这就解决了使用
sudo
以root身份写入文件的问题。现在,让我们详细讨论一下在shell脚本中输出包含换行符文本的各种方法。我的首选解决方案是将here-document输入到上述
sudo tee
命令中;这样就不需要使用
cat
、
echo
、
printf
或任何其他命令了。单引号移到了sentinel introduction
<<'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
如果你在
echo
后面加上
-e
(并且你使用的是支持非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可能只会回显一个带有大量字面值\n
的字符串,而紧随其后的是一个字面值-e
。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
之类的序列转换为文字换行符。这意味着这样的字符串适用于任何命令,包括普通旧式的无-e
的echo
命令:
echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' |
sudo tee -a /etc/pacman.conf >/dev/null
但是,虽然比echo -e
更便携,ANSI引号仍然是一个非POSIX扩展。
再次强调,虽然这些都是选项,但我更喜欢上面直接使用tee <<EOF
的解决方案。
sudo bash -c 'foo >/some/file'
可行 :-) - kayochincat <<EOF | sudo tee -a /some/file > /dev/null ...
- ltvancat
进程。 :) - Mark Reed