将stdout重定向到您没有写权限的文件


113

当您尝试修改文件而没有写权限时,会出现错误:

> touch /tmp/foo && sudo chown root /tmp/foo
> echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Sudoing无济于事,因为它以root身份运行命令,但是shell仍会处理重定向标准输出并以您自己的方式打开文件:

> sudo echo test > /tmp/foo
zsh: permission denied: /tmp/foo

除了以root用户身份打开Shell并以这种方式操作文件之外,是否有一种简单的方法可以将stdout重定向到您没有写入权限的文件?

> sudo su
# echo test > /tmp/foo

2
答案从StackOverflow的类似的问题stackoverflow.com/questions/82256/...
克里斯蒂安Ciupitu

您如何不拥有在tmp中创建的文件的权限?是因为umask吗?
k961

@ k961我曾经chown更改过所有者;这只是一个例子
Michael Mrozek

Answers:


116

是的,使用tee。所以echo test > /tmp/foo变成

echo test | sudo tee /tmp/foo

您也可以附加(>>

echo test | sudo tee -a /tmp/foo

27
Tee也将输出到stdout;有时,您不希望内容填满整个屏幕。要解决此问题,请做echo test | sudo tee /tmp/foo > /dev/null
Shawn J. Goff 2010年

您将如何使用Heredoc?
JohnDoea

26

将文件内容替换为输出echo(例如>shell重定向操作符)。

echo test | sudo dd of=/tmp/foo

要写入文件(在开始时,虽然可以使用seek以不同的偏移量输出)而不会被截断(例如1<>Bourne shell运算符):

echo test | sudo dd of=/tmp/foo conv=notrunc

>>使用GNU 附加到文件(如)dd

echo test | sudo dd of=/tmp/foo oflag=append conv=notrunc

另请参见GNU ddconv=excl以避免破坏现有文件(例如set -o noclobberPOSIX shell中的文件),而避免conv=nocreat相反(仅更新现有文件)。


聪明!这减轻了echo test | sudo tee /tmp/foo >/dev/null丢弃输出的需要。
亚当·卡兹

2
我可能不得不把它拿回来;除非您使用晦涩的仅限GNU的选项,否则这样做dd是不可靠的,因为iflag=fullblock oflag=fullblock这会消除此答案的复杂性。我会坚持的tee
亚当·卡兹

5
dd具有可靠的bs = 1是可靠的
umeboshi 2015年

2
@umeboshi但是只有当您有足够的经验来确切地知道您在做什么时,它才是可靠的。dd如果只犯了一个错误,那么for 可能是相当危险的(如果不说:毁灭性的)。因此,对于新用户,我宁愿推荐这种tee方法。
语法错误

1
@AdamKatz,在dd of=file单独的情况下(不带count/ skip...),它是可靠的。iflag=fullblock不需要,因为这里dd在输出上写入了在输入上读取的内容。它是否不是完整块并不重要。
斯特凡Chazelas

12

tee 也许是最好的选择,但是根据您的情况,这样的事情可能就足够了:

sudo sh -c 'echo test > /tmp/foo'

1
问题3:eval而不是简单sh -c;-i,这将更改您的工作目录;以root用户身份运行整个命令,这可能会改变其行为或可能带来不必要的风险。为什么这会引起反对?

4
您没有这样做,但是它具有误导性,通常很糟糕,甚至可能很危险。

5

虽然我同意,但这| sudo tee是规范的方法,有时sed(在此假设GNU sed)可能会起作用:

cat sudotest 
line 1

sudo sed -i '1iitest' sudotest && cat sudotest 
itest
line 1

sudo sed -i '$aatest' sudotest && cat sudotest 
itest
line 1
atest

-i在适当位置修改文件。1i表示在第1行之前插入$a表示在最后一行之后附加

或复制到xclipboard:

somecommand | xclip
sudo gedit sudotest
move cursor to desired place, click middle mouse button to insert, save

1
这句话就像是中国古代的谜语。看不清楚这是怎么得到这么多赞成的。(hrmph
语法错误

1
@syntaxerror:先生,对不起,我不是英语母语人士。如果您指定不清楚的地方,我可以尝试改善答案。与Gert的65票相比,似乎4票也没有那么多,你不觉得吗?
用户未知,

好吧,我会对4 :-D非常满意。您的英语一点也不差。它只是用一种简洁的技术书呆子说话。我必须猜你是编码员。彼此之间的编码人员将以这种方式完全理解自己,但是非编码人员必须认为它的措辞就像...所说的。
语法错误,2016年

请注意,sed -i实际上并不会修改文件 -它会创建一个临时文件,并在退出时将其重命名。所以,你将无法做到像tail -f ...原始文件,并使用查看输出sed -i ...,同时在管道运行
安德鲁汉勒

@AndrewHenle:是的,因为大小可能会增加或缩小,而且大多数sed调用都可能是这种情况,而且您甚至无法-afaik-写入SSD上的同一位置,这只是伪的“就地”操作。作为非英语母语者,我是否可以要求简短的表达,这不太可能会被误解?只是-i creates a new file of same name还是有更紧凑的东西?我想我喜欢in place,因为它解释了i。gnu-sed联机帮助页也将其调用到位,并且长标记为--in-place
用户未知

2

我一直在脑海里想出一个类似的问题,并提出了以下解决方案:

  • sudo uncatuncat一个程序在哪里读取标准输入并将其写入命令行中命名的文件,但我尚未编写uncat

  • sudocatsudoedit我尚未编写的变种,它可以做一个清洁工sudo cat或清洁工sudo uncat

  • sudoedit与作为外壳程序脚本的EDITOR一起使用的小技巧

    #!/bin/sh
    # uncat
    cat > "$1"
    

    可以将其作为|sudo ./uncat file或调用,| EDITOR=./uncat sudoedit但会产生有趣的副作用。


cat需要的文件列表精读 inate,因此uncat应采取文件的列表 CON inate来。它必须使用魔术来决定每个文件中要放多少。另一个名称包括dogto-fileredirect
ctrl-alt-delor

我想不出有什么理由想要uncat什么tee
2015年

1
好吧,它tee具有一个琐碎的缺点,那就是将其stdin写入其stdout中-通过将stdout重定向到,可以轻松地缓解这种情况/dev/null。其他替代方法包括dd of=/tmp/foo(在另一个答案中提到),该方法将状态信息写入stderr,以及cp /dev/stdin /tmp/foo
斯科特,

1

使用moreutils包装中的海绵。它具有不写入标准输出的优点。

echo test | sudo sponge /tmp/foo

使用-a选项可以附加到文件而不是覆盖文件。


1
我不确定不写stdout会带来什么好处,但是您可以将输出重定向到/dev/null是否有问题
Michael Mrozek

1
当然,我可以重定向到/ dev / null,但是该命令更易于阅读和键入,而无需重定向。不写到stdout的优点是我的终端没有垃圾。
洪特瓦里·莱文特

1
我不会安装软件包来节省重定向,但是我经常使用海绵,因此它已经存在。
洪特瓦里·莱文特

0

错误来自外壳程序执行操作的顺序。

重定向在外壳程序执行之前就已进行了处理sudo,因此,该重定向是在您当前正在使用的用户权限下完成的。由于您没有写权限来创建/截断重定向目标,因此您会permission denied从Shell中收到错误消息。

解决方案是保证输出文件是使用赋予您的身份创建的sudo,例如tee

$ generate_output | sudo tee target_file
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.