如何使用sudo将输出重定向到我没有写入权限的位置?

我在我们的一个开发RedHat linux盒子上获得了sudo访问权限,我似乎发现自己经常需要将输出重定向到我通常没有写入权限的位置。

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

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

我刚收到回复:

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

我怎么才能让它工作?

308070 次浏览

写剧本怎么样?

文件名:myscript

#!/bin/sh
/bin/ls -lah /root > /root/test.out
# end script

然后使用sudo运行脚本:

sudo ./myscript

让sudo运行一个shell,像这样:

sudo sh -c "echo foo > ~root/out"

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

有多种解决方案:

  • 使用sudo运行shell并使用-c选项向其发出命令:

    sudo sh -c 'ls -hal /root/ > /root/test.out'
  • Create a script with your commands and run that script with sudo:

    #!/bin/shls -hal /root/ > /root/test.out

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

  • 使用sudo -s启动shell,然后运行您的命令:

    [nobody@so]$ sudo -s[root@so]# ls -hal /root/ > /root/test.out[root@so]# ^D[nobody@so]$
  • Use sudo tee (if you have to escape a lot when using the -c option):

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

    需要重定向到/dev/null才能阻止<强>发球输出到屏幕。到追加而不是覆盖输出文件(>>),使用tee -atee --append(最后一个特定于GNUcoreutils)。

感谢jdAdam J. Forster乔纳森的第二,第三和第四个解决方案。

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

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

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

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

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

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

ctrl+d

chmod a+x myscript.shsudo myscript.sh

希望有帮助。

这里有人刚刚建议sudo t:

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

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

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

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"
  • 安排具有所需权限的管道/子shell(即sudo)

    # Read and append to a filecat ./'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';

我自己想出来的一个把戏是

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

我会这样做:

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

关于主题的另一个变化:

sudo bash <<EOFls -hal /root/ > /root/test.outEOF

或者,当然:

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

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

澄清一下为什么发球选项更可取

假设您有适当的权限来执行创建输出的命令,如果您将命令的输出通过管道传输到tec,您只需要使用sudo和直接tec提升tec的权限即可写入(或追加)相关文件。

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

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

举几个更实际的例子:

# kill off one source of annoying advertisementsecho 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选项来将追加(如>>)附加到目标文件,而不是覆盖它(如>)。

这是基于涉及tee的答案。为了使事情更容易,我写了一个小脚本(我称之为suwrite)并在+x权限下将其放在/usr/local/bin/中:

#! /bin/shif [ $# = 0 ] ; thenecho "USAGE: <command writing to stdout> | suwrite [-a] <output file 1> ..." >&2exit 1fifor arg in "$@" ; doif [ ${arg#/dev/} != ${arg} ] ; thenecho "Found dangerous argument ‘$arg’. Will exit."exit 2fidonesudo tee "$@" > /dev/null

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

echo test | suwrite /root/test.txt

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

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

它也有一些简单的保护,防止写入/dev/设备,这是本页上的评论中提到的一个问题。

我对这个问题的看法是:

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

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

如果您需要附加到文件:

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

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

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

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

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

sudo at nowat> echo test > /tmp/test.outat> <EOT>job 1 at Thu Sep 21 10:49:00 2017