关于linux:如何使用sudo将输出重定向到我无权写入的位置?

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

我已经在我们的一个开发Redhat Linux设备上获得了sudo访问权,而且我似乎经常需要将输出重定向到我通常没有写访问权的位置。

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

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

我刚收到回复:

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

我怎样才能让这个工作?


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

有多种解决方案:

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

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

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

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

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

    1
    2
    3
    4
    [nobody@so]$ sudo -s
    [root@so]# ls -hal /root/ > /root/test.out
    [root@so]# ^D
    [nobody@so]$
  • 使用sudo tee(如果在使用-c选项时需要大量逃逸):

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

    需要重定向至/dev/null,以阻止tee输出到屏幕。追加而不是覆盖输出文件(>>,使用tee -atee --append(最后一个特定于gnu coreutils)。

感谢JD、Adam J.Forster和Johnathan提供第二、第三和第四个解决方案。


这里有人刚刚推荐了sudoing tee:

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

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


我自己发现的一个诀窍是

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


问题是命令在sudo下运行,但重定向在用户下运行。这是由壳牌公司完成的,你几乎无能为力。

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

绕过这一点的通常方法是:

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

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

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

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

    1
    sudo bash -c"{ command1 arg; command2 arg; } > /log/file.txt"

这一主题的另一个变化是:

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

当然,

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

他们的优势很小,你不需要记住任何关于sudoshbash的论点。


解释一下为什么选择发球

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

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

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

对于几个更实际的例子:

1
2
3
4
5
6
7
# 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"
IPADDR=10.42.84.168
PREFIX=24" | sudo tee -a /etc/sysconfig/network-scripts/ifcfg-eth4

在这些示例中,您将获取非特权命令的输出,并将其写入通常只能由根写入的文件,这是您问题的根源。

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

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


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

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

我处理这个问题的方式是:

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

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

如果需要附加到文件:

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


写剧本怎么样?

文件名:myscript

1
2
3
4
5
#!/bin/sh

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

# end script

然后使用sudo运行脚本:

1
sudo ./myscript

我会这样做:

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


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

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

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


每当我要做这样的事情时,我都会变得根深蒂固:

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

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


这是基于涉及tee的答案。为了使事情更简单,我写了一个小脚本(我称之为suwrite),并在+x许可下放入/usr/local/bin/

1
2
3
4
5
6
7
8
9
10
11
12
#! /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

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

1
echo test | suwrite /root/test.txt

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

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

它还具有一些简单的保护措施,防止向/dev/设备写入内容,这是本页评论中提到的一个问题。


也许你只允许sudo访问一些程序/路径?那就没有办法做你想做的事。(除非你以某种方式破解)

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

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

ctrl+d

1
2
chmod a+x myscript.sh
sudo myscript.sh

希望能有所帮助。


1
2
3
4
sudo at now  
at> echo test > /tmp/test.out  
at> <EOT>  
job 1 at Thu Sep 21 10:49:00 2017