如何使用 sudo 将输出重定向到我无权写入的位置?[关闭]

How do I use sudo to redirect output to a location I don't have permission to write to? [closed]

提问人:Jonathan 提问时间:9/17/2008 最后编辑:Jonathan 更新时间:12/6/2021 访问量:330545

问:


这个问题似乎与特定的编程问题、软件算法或程序员主要使用的软件工具无关。如果您认为该问题在另一个 Stack Exchange 站点上是主题,您可以发表评论以解释在哪里可以回答该问题。

2年前关闭。

去年,社群审查了是否要重新讨论这个问题,并关闭了这个问题:

原始关闭原因未解决

我被授予了我们开发的 RedHat linux 机器之一的 sudo 访问权限,我似乎发现自己经常需要将输出重定向到我通常没有写入权限的位置。

问题是,这个人为的例子不起作用:

sudo ls -hal /root/ > /root/test.out

我刚刚收到回复:

-bash: /root/test.out: Permission denied

我怎样才能让它工作?

Linux Bash 权限 sudo io-redirection

评论

1赞 Szabolcs Dombi 7/10/2016
使用 chmod u+w 文件名
0赞 Jonathan 7/10/2016
@DombiSzabolcs 你是建议我先将文件创建为 sudo,然后给自己权限?好主意。
2赞 tripleee 2/14/2019
在许多情况下,您最终会来到这里,因为您问“为什么我的权限被拒绝了?有时答案是,您确实需要创建一个文件(在这种情况下,请继续阅读答案),但很多时候,您只需要在其他地方创建文件,就像您自己一样。root
3赞 TingQian LI 8/1/2019
在为这些答案而苦苦挣扎之后,我最终选择重定向到临时文件并 sudo 将其移动到目的地。
0赞 Nate Eldredge 5/6/2020
另请参阅 stackoverflow.com/questions/84882/...

答:

6赞 Jd 9/17/2008 #1

写剧本怎么样?

文件名: myscript

#!/bin/sh

/bin/ls -lah /root > /root/test.out

# end script

然后使用 sudo 运行脚本:

sudo ./myscript
26赞 Penfold 9/17/2008 #2

让 sudo 运行一个 shell,如下所示:

sudo sh -c "echo foo > ~root/out"
1535赞 Cristian Ciupitu 9/17/2008 #3

您的命令不起作用,因为重定向是由您的 shell 执行的,而 shell 没有写入 的权限。sudo 执行输出的重定向。/root/test.out

有多种解决方案:

  • 使用 sudo 运行一个 shell,并使用以下选项向其发出命令:-c

    sudo sh -c 'ls -hal /root/ > /root/test.out'
    
  • 使用命令创建一个脚本,并使用 sudo 运行该脚本:

    #!/bin/sh
    ls -hal /root/ > /root/test.out
    

    跑。如果您不想创建临时文件,请参阅 Steve Bennett 的回答sudo ls.sh

  • 启动一个 shell,然后运行你的命令:sudo -s

    [nobody@so]$ sudo -s
    [root@so]# ls -hal /root/ > /root/test.out
    [root@so]# ^D
    [nobody@so]$
    
  • 使用(如果您在使用该选项时必须进行大量转义):sudo tee-c

    sudo ls -hal /root/ | sudo tee /root/test.out > /dev/null
    

    需要重定向到以阻止 T 恤输出到屏幕。追加而不是覆盖输出文件 ()、use 或 (最后一个是特定于 GNU coreutils 的)。/dev/null>>tee -atee --append

感谢 JdAdam J. ForsterJohnathan 提供第二、第三和第四种解决方案。

评论

3赞 curious_prism 6/13/2013
这里有一个很好的答案,告诉你如何分别重定向 STDERR 和 STDOUT:stackoverflow.com/questions/692000/......基本上perl -e 'print "STDIN\n"; print STDERR "STDERR\n"; ' > >( tee stdout.log ) 2> >( tee stderr.log >&2 )
2赞 Urhixidur 2/15/2014
你会想做'sudo -E ...'为了在 shelled out 命令中使用变量(例如,在脚本中使用它时)。
3赞 thomasrutter 7/7/2015
在很多情况下,将 tee 输出重定向到 /dev/null 可能没有必要,因为将输出回显到屏幕是无害的。例如,当仅处理常规命令的输出或小文本文件的内容时。
0赞 dinesh saini 4/26/2022
供参考:除了我发现其他灵魂之外,我在 heredoc 语句中发现了良好的 shell 扩展工作原理,因此允许命令在非 root 环境中执行,并最终允许所有结果作为 sudo 重定向到 priviledged 文件,这里的文件是 root。teesudo bash <<EOF echo -e "$(whoami)\n$(ls -l)\n$(pstree -sp $$)" > a whoami pstree -sp $$ EOFa
5赞 Adam J. Forster 9/17/2008 #4

每当我必须做这样的事情时,我就会成为root:

# sudo -s
# ls -hal /root/ > /root/test.out
# exit

这可能不是最好的方法,但它有效。

1赞 user15453 9/17/2008 #5

也许您只获得了对某些程序/路径的 sudo 访问权限?那么就没有办法做你想做的事了。(除非你会以某种方式破解它)

如果不是这种情况,那么也许你可以编写 bash 脚本:

cat > myscript.sh
#!/bin/sh
ls -hal /root/ > /root/test.out 

按 + :ctrld

chmod a+x myscript.sh
sudo myscript.sh

希望对你有所帮助。

140赞 Jonathan 9/17/2008 #6

这里有人刚刚建议 sudoing tee:

sudo ls -hal /root/ | sudo tee /root/test.out > /dev/null

这也可用于将任何命令重定向到您无权访问的目录。它之所以有效,是因为 tee 程序实际上是一个“文件回显”程序,重定向到 /dev/null 是为了阻止它也输出到屏幕,以保持它与上面的原始人为示例相同。

评论

13赞 Hagen von Eitzen 8/26/2016
在许多情况下,即如果普通用户有权执行命令,并且“仅”无法写入所需的输出文件,则第一个(即命令本身)可能会被省略sudo
0赞 cyqsimon 3/9/2023
还有来自包装。工作方式相同,只是它不输出到,因此您可以放弃重定向到 。所以它只是.spongemoreutilsteestdout/dev/nullls -hal /root/ | sudo sponge /root/test.out
65赞 dsm 9/23/2008 #7

问题是命令在 下运行,但重定向在您的用户下运行。这是由 shell 完成的,您对此无能为力。sudo

sudo command > /some/file.log
`-----v-----'`-------v-------'
   command       redirection

绕过此问题的常用方法是:

  • 将命令包装在脚本中,并在 sudo 下调用。

    如果命令和/或日志文件发生更改,您可以执行 脚本将这些作为参数。例如:

    sudo log_script command /log/file.txt
    
  • 调用 shell 并将命令行作为参数传递-c

    这对于一次性复合命令特别有用。 例如:

    sudo bash -c "{ command1 arg; command2 arg; } > /log/file.txt"
    
  • 安排具有所需权限的管道/子壳(即 sudo)

    # Read and append to a file
    cat ./'file1.txt' | sudo tee -a '/log/file.txt' > '/dev/null';
    
    # Store both stdout and stderr streams in a file
    { command1 arg; command2 arg; } |& sudo tee -a '/log/file.txt' > '/dev/null';
    
100赞 rhlee 11/21/2011 #8

我自己想出的一个技巧是

sudo ls -hal /root/ | sudo dd of=/root/test.out

评论

21赞 kristianlm 3/6/2013
sudo dd比上面的例子要好!sudo tee /root/file > /dev/null
17赞 rhlee 7/25/2013
dd 不是一个奇怪的晦涩难懂的命令。每当您需要在两个块设备之间缓冲复制大量数据时,都会使用它。语法实际上非常简单,是命令名称,是告诉输出文件是什么的参数。ddof=/root/test.outdd
15赞 blockloop 4/15/2014
@steve 一切都是“晦涩难懂”的,直到你了解它是什么。 表示输出文件,是 Linux 和 OSX 中使用的非常流行的工具(主要用于将图像写入磁盘)。这肯定是一个巧妙的技巧。ofdd
14赞 thomasrutter 7/7/2015
dd可能和这里一样有用。在这两种情况下,您都使用一个通用的、众所周知的命令来实现一个目的,尽管该命令与其原始预期目的略有不同,但仍然是众所周知的且有据可查的。虽然擅长复制大量数据,但它不会因少量数据而吝啬。它的好处是不会将输出回显到标准输出。teedd
38赞 ali_m 1/6/2016
每当你把单词放在一起输入时,你都要非常非常确定后面的参数是正确的(特别是考虑到它的非标准语法)。他们不会无缘无故地称它为“磁盘破坏者”......sudo dd
5赞 fardjad 4/21/2013 #9

我会这样做:

sudo su -c 'ls -hal /root/ > /root/test.out'

评论

3赞 Steve Bennett 5/13/2013
这实际上看起来更简洁一些,因为您不需要显式指定 shell。
1赞 pabouk - Ukraine stay strong 7/28/2013
一个小缺点是它运行一个额外的进程():su$ sudo su -c 'pstree -sp $$ >/dev/fd/1' init(1)───gnome-terminal(6880)───bash(6945)───sudo(401)───su(402)───bash(410)───pstree(411)
31赞 Steve Bennett 5/13/2013 #10

主题的另一个变体:

sudo bash <<EOF
ls -hal /root/ > /root/test.out
EOF

或者当然:

echo 'ls -hal /root/ > /root/test.out' | sudo bash

它们有一个(微小的)优势,即您不需要记住任何参数或sudosh/bash

27赞 jg3 11/2/2013 #11

澄清一下为什么 T 恤选项更可取

假设您具有执行创建输出的命令的适当权限,如果将命令的输出通过管道传递给 tee,则只需使用 sudo 提升 tee 的权限,并指示 tee 写入(或追加)到相关文件。

在问题中给出的示例中,这意味着:

ls -hal /root/ | sudo tee /root/test.out

举几个更实际的例子:

# kill off one source of annoying advertisements
echo 127.0.0.1 ad.doubleclick.net | sudo tee -a /etc/hosts

# configure eth4 to come up on boot, set IP and netmask (centos 6.4)
echo -e "ONBOOT=\"YES\"\nIPADDR=10.42.84.168\nPREFIX=24" | sudo tee -a /etc/sysconfig/network-scripts/ifcfg-eth4

在每一个示例中,您都获取非特权命令的输出,并写入通常只能由 root 写入的文件,这就是问题的根源。

这样做是个好主意,因为生成输出的命令不是使用提升的权限执行的。这里似乎无关紧要,但是当源命令是您不完全信任的脚本时,它至关重要。echo

请注意,您可以使用 -a 选项将 append (like ) 附加到目标文件,而不是覆盖它 (like )。>>>

评论

0赞 Jonathan 11/6/2013
对不起 js3,但这已经有人提出过(早在 2008 年),并且位于第二高的答案:stackoverflow.com/a/82553/6910
3赞 jg3 11/8/2013
你是对的,乔纳森,我将更新我的答案,以扩展为什么这是一个更可取的选择的原因。感谢您的有用反馈。
3赞 jamadagni 11/27/2013 #12

这是基于涉及 的答案。为了让事情变得更容易,我写了一个小脚本(我称之为 ),并在获得许可的情况下将其放入:teesuwrite/usr/local/bin/+x

#! /bin/sh
if [ $# = 0 ] ; then
    echo "USAGE: <command writing to stdout> | suwrite [-a] <output file 1> ..." >&2
    exit 1
fi
for arg in "$@" ; do
    if [ ${arg#/dev/} != ${arg} ] ; then
        echo "Found dangerous argument ‘$arg’. Will exit."
        exit 2
    fi
done
sudo tee "$@" > /dev/null

如代码中的 USAGE 所示,您所要做的就是将输出通过管道传递给此脚本,后跟所需的超级用户可访问文件名,如果需要,它会自动提示您输入密码(因为它包括 )。sudo

echo test | suwrite /root/test.txt

请注意,由于这是一个简单的包装器,它也将接受 tee 的附加选项,并且还支持同时写入多个文件。tee-a

echo test2 | suwrite -a /root/test.txt
echo test-multi | suwrite /root/test-a.txt /root/test-b.txt

它还具有一些简单的保护功能,以防止写入设备,这是本页评论之一中提到的一个问题。/dev/

15赞 Nikola Petkanski 6/25/2016 #13

我解决这个问题的方式是:

如果需要写入/替换文件:

echo "some text" | sudo tee /path/to/file

如果需要附加到文件:

echo "some text" | sudo tee -a /path/to/file

评论

2赞 Jonathan 6/25/2016
这与上面的答案有何不同?
2赞 jamadagni 2/4/2019
它没有“实质性的区别”,但它阐明了替换和附加到文件之间的区别用法。
2赞 MortimerCat 2/19/2020
这是我复制到备忘单中的答案。
14赞 haridsv 7/28/2016 #14

不是要打死马,但这里有太多的答案使用 ,这意味着除非你想在屏幕上看到副本,否则你必须重定向到。teestdout/dev/null

一个更简单的解决方案是像这样使用:cat

sudo ls -hal /root/ | sudo bash -c "cat > /root/test.out"

请注意重定向是如何放在引号内的,以便它由启动的 shell 而不是运行它的 shell 进行评估。sudo

评论

7赞 Nick Russo 12/9/2016
我认为它没有得到太多的爱,因为它并不比.使用 T 恤可以避免需要外壳,而 cat 则不需要。sudo bash -c "ls -hal /root > /root/test.out"
2赞 user8648126 9/21/2017 #15
sudo at now  
at> echo test > /tmp/test.out  
at> <EOT>  
job 1 at Thu Sep 21 10:49:00 2017  

评论

1赞 tripleee 6/29/2018
如果你有,没有用或没有必要。 更高效、更优雅地执行相同的操作(但仍然存在一个缺陷,即您可能正在运行不需要该权限的东西;您通常应该在可能的情况下避免使用特权命令)。sudoatsudo sh -c 'echo test >/tmp/test.out'root