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


880

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

麻烦的是,这个人为的例子不起作用:

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

我刚收到回复:

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

我该如何工作?


1
使用chmod u + w文件名
Szabolcs Dombi

@DombiSzabolcs您建议我先将文件创建为sudo,然后再授予自己权限?好主意。
乔纳森

在许多情况下,您最终会出现在这里,是因为您问“为什么我会拒绝权限?” 有时答案是您确实需要创建一个文件root(在这种情况下,请继续阅读答案),但是通常,您只需要像其他人一样在其他位置创建文件。
Tripleee

1
经过这些答案的挣扎之后,我最终选择了重定向到临时文件并将其sudo移动到目标位置。
李廷谦

Answers:


1231

您的命令无效,因为重定向是由您的外壳执行的,该外壳没有写权限/root/test.out。sudo 执行输出的重定向。

有多种解决方案:

  • 使用sudo运行shell并使用以下-c选项向其提供命令:

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

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

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

  • 使用启动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
    

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

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


1
这里有一个很好的答案,告诉您如何分别重定向STDERR和STDOUT:stackoverflow.com/questions/692000/… ...perl -e 'print "STDIN\n"; print STDERR "STDERR\n"; ' > >( tee stdout.log ) 2> >( tee stderr.log >&2 )
errant.info 2013年

2
您需要执行'sudo -E ...'以便在带壳命令中使用变量(例如,在脚本中使用此变量时)。
Urhixidur 2014年

3
在很多情况下,将输出回显到屏幕是无害的,可能不需要将tee输出重定向到/ dev / null。例如,仅处理常规命令的输出或小型文本文件的内容时。
thomasrutter

105

这里有人建议将steeing tee:

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

这也可以用于将任何命令重定向到您无权访问的目录。之所以起作用,是因为tee程序实际上是一个“回显文件”程序,并且重定向到/ dev / null就是要停止它并将其输出到屏幕上,以使其与上面的原始设计示例相同。


5
在许多情况下,即如果普通用户具有执行该命令的权限,并且“仅”不能写入所需的输出文件,则sudo可以省略第一个(即对于命令本身)
Hagen von Eitzen

81

我发现自己的一个窍门是

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

16
sudo ddsudo tee /root/file > /dev/null上面的例子好!
kristianlm

14
dd不是一个奇怪的命令。每当需要在两个块设备之间使用缓冲区复制大量数据时,都可以使用它。语法实际上非常简单,dd是命令名称,of=/root/test.outdd表明输出文件是什么的参数。
rhlee

14
@steve一切都是“晦涩的”,直到您了解它是什么。of表示输出文件dd是Linux和OSX中都非常流行的工具(主要用于将映像写入磁盘)。当然,这是一个巧妙的技巧。
blockloop

12
dd可能和tee这里一样有用。在这两种情况下,您都使用一个通用的,众所周知的命令,其目的虽然与最初的预期目的略有不同,但仍然是众所周知的且有据可查。虽然dd擅长复制大量数据,但它不会吸收少量数据。这样做的好处是也不会将输出回显到标准输出。
thomasrutter

26
每当键入sudo dd彼此相邻的单词时,您都希望非常非常确定后面的参数正确(特别是考虑到其非标准语法)。他们不称其为“磁盘破坏者”……
ali_m

45

问题是命令在下运行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"
    

23

主题的另一种变化:

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

或者,当然:

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

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


18

澄清为什么最好使用tee选项

假设您具有执行创建输出的命令的适当权限,如果将命令的输出通过管道传输到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写入的文件,而root是您问题的出处。

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

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


抱歉,js3,但是已经有人提出(早在2008年),它的回答排在第二位:stackoverflow.com/a/82553/6910
Jonathan

2
您说得对,乔纳森(Jonathan),我将更新我的答案,以扩展为什么这是更可取的选择的原因。感谢您的有用反馈。
jg3

17

使sudo运行shell,如下所示:

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

11

我要处理此问题的方法是:

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

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

如果需要附加到文件:

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

1
这与上面的答案有何本质不同?
乔纳森

2
它不是“基本不同”,但它阐明了替换和附加到文件之间的独特用法。
jamadagni

1
这是我复制到备忘单上的答案。
MortimerCat

5

编写脚本怎么样?

文件名:myscript

#!/bin/sh

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

# end script

然后使用sudo运行脚本:

sudo ./myscript

4

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

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

这可能不是最好的方法,但是可以。


4

我会这样:

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

2
实际上,这看起来有点干净,因为您无需显式指定shell。
史蒂夫·本内特

1
一个小缺点是它运行一个附加过程(su): $ sudo su -c 'pstree -sp $$ >/dev/fd/1' init(1)───gnome-terminal(6880)───bash(6945)───sudo(401)───su(402)───bash(410)───pstree(411)
pabouk

3

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

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

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

请注意,如何将重定向放在引号中,以便由启动外壳程序sudo而不是运行该外壳程序的外壳程序进行评估。


这很好。我不明白为什么它要投反对票。已投票。
Teemu Leisti

4
我认为爱情并没有太多,因为它没有比爱情更好的了sudo bash -c "ls -hal /root > /root/test.out"。使用tee避免需要外壳,而cat则不需要。
尼克·鲁索

2

这是基于涉及的答案tee。为了使事情变得容易,我编写了一个小脚本(我称之为suwrite),并在/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/设备的简单保护措施,这是此页评论之一中提到的一个问题。


1

也许您只能通过sudo访问某些程序/路径?那就没有办法做你想做的。(除非您会以某种方式破解它)

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

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

ctrl+ d

chmod a+x myscript.sh
sudo myscript.sh

希望对您有所帮助。


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

1
如果你有sudo反正at是没有用的或必要的。sudo sh -c 'echo test >/tmp/test.out'这样做的效率和优雅度更高(但仍然存在缺陷,即您可能正在运行root不需要该特权的事物;通常应尽可能避免使用特权命令)。
Tripleee '18
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.