Como uso o sudo para redirecionar a saída para um local no qual não tenho permissão para gravar?

881

Recebi acesso ao sudo em uma de nossas caixas Linux RedHat de desenvolvimento e parece que muitas vezes preciso redirecionar a saída para um local ao qual normalmente não tenho acesso de gravação.

O problema é que este exemplo artificial não funciona:

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

Acabei de receber a resposta:

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

Como posso fazer isso funcionar?

Jonathan
fonte
1
usar chmod u + w filename
Szabolcs Dombi
@DombiSzabolcs Você está sugerindo que eu crie o arquivo como sudo primeiro e depois me dê permissão? Boa ideia.
10136 Jonathan
Em muitas situações, você acaba aqui porque pergunta "por que a permissão é negada?" Às vezes, a resposta é que você precisa criar um arquivo como root(nesse caso, continue lendo as respostas), mas com muita frequência, basta criar o arquivo em outro lugar, como você.
Tripleee
1
Depois de lutar com essas respostas, eu finalmente escolhi redirecionar para um arquivo temporário e o sudo o move para o destino.
TingQian LI
Veja também stackoverflow.com/questions/84882/…
Nate Eldredge

Respostas:

1233

Seu comando não funciona porque o redirecionamento é realizado pelo seu shell, que não tem permissão para gravar /root/test.out. O redirecionamento da saída não é realizado pelo sudo.

Existem várias soluções:

  • Execute um shell com o sudo e forneça o comando usando a -copção:

    sudo sh -c 'ls -hal /root/ > /root/test.out'
    
  • Crie um script com seus comandos e execute esse script com sudo:

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

    Corra sudo ls.sh. Veja a resposta de Steve Bennett se você não quiser criar um arquivo temporário.

  • Inicie um shell sudo -se execute seus comandos:

    [nobody@so]$ sudo -s
    [root@so]# ls -hal /root/ > /root/test.out
    [root@so]# ^D
    [nobody@so]$
    
  • Use sudo tee(se você precisar escapar muito ao usar a -copção):

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

    O redirecionamento para /dev/nullé necessário para impedir a saída do tee para a tela. Para acrescentar em vez de sobrescrever o arquivo de saída ( >>), use tee -aou tee --append(o último é específico para os coreutils do GNU ).

Agradecemos a Jd , Adam J. Forster e Johnathan pela segunda, terceira e quarta soluções.

Cristian Ciupitu
fonte
1
Há uma grande resposta que lhe diz como redirecionar STDERR e STDOUT separadamente aqui: stackoverflow.com/questions/692000/... ... basicamenteperl -e 'print "STDIN\n"; print STDERR "STDERR\n"; ' > >( tee stdout.log ) 2> >( tee stderr.log >&2 )
errant.info
2
Você desejará fazer 'sudo -E ...' para usar variáveis ​​no comando shelled out (ao usar isso em um script, por exemplo).
Urhixidur
3
Redirecionar a saída tee para / dev / null provavelmente não é necessário em muitos casos em que o eco da saída na tela é inofensivo. Por exemplo, ao lidar apenas com a saída de comandos regulares ou o conteúdo de pequenos arquivos de texto.
thomasrutter
105

Alguém aqui acabou de sugerir sudoing tee:

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

Isso também pode ser usado para redirecionar qualquer comando para um diretório ao qual você não tem acesso. Ele funciona porque o programa tee é efetivamente um programa de "eco para um arquivo" e o redirecionamento para / dev / null é para impedir que ele também seja exibido na tela para mantê-lo igual ao exemplo original inventado acima.

Jonathan
fonte
5
Em muitos casos, ou seja, se o usuário normal tem prmissions para executar o comando e "apenas" não pode escrever para o arquivo de saída desejado, o primeiro sudo(ou seja, para o próprio comando) pode ser omitido
Hagen von Eitzen
81

Um truque que eu descobri foi

sudo ls -hal /root/ | sudo dd of=/root/test.out
rhlee
fonte
16
sudo ddé melhor que os sudo tee /root/file > /dev/nullexemplos acima!
precisa
14
dd não é um comando obscuro estranho. É usado sempre que você precisa copiar grandes quantidades de dados com buffer entre dois dispositivos de bloco. A sintaxe é realmente bastante simples, ddé o nome do comando, of=/root/test.outé o argumento que informa o ddque são os arquivos de saída.
rhlee
14
@steve Tudo é 'obscuro' até você aprender o que é. ofsignifica arquivo de saída e ddé uma ferramenta muito popular usada no Linux e no OSX (principalmente para gravar imagens em discos). Este é um truque interessante, com certeza.
blockloop
12
ddé provavelmente igualmente útil como teeaqui. Nos dois casos, você está usando um comando comum e conhecido para uma finalidade que, embora ligeiramente diferente da finalidade original pretendida, ainda é bem conhecida e documentada. Embora ddseja bom em copiar grandes quantidades de dados, não é ruim com pequenas quantidades. Aqui tem o benefício de não fazer eco da saída para a saída padrão.
thomasrutter
26
Sempre que você digita palavras sudo ddpróximas umas das outras, deseja ter muita, muita certeza de que os argumentos a seguir estão corretos (especialmente considerando sua sintaxe não padrão). Eles não chamam de "destruidor de disco" por nada ...
ali_m 5/16
45

O problema é que o comando é executado sob sudo, mas o redirecionamento é executado sob o seu usuário. Isso é feito pelo shell e há muito pouco que você pode fazer sobre isso.

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

As formas usuais de contornar isso são:

  • Envolva os comandos em um script que você chama sob sudo.

    Se os comandos e / ou o arquivo de log forem alterados, você poderá fazer com que o script os aceite como argumentos. Por exemplo:

    sudo log_script command /log/file.txt
    
  • Chame um shell e passe a linha de comando como um parâmetro com -c

    Isso é especialmente útil para comandos compostos únicos. Por exemplo:

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

Ainda outra variação sobre o tema:

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

Ou claro:

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

Eles têm a (pequena) vantagem de que você não precisa se lembrar de quaisquer argumentos para sudoou sh/bash

Steve Bennett
fonte
18

Esclarecendo um pouco sobre por que a opção tee é preferível

Supondo que você tenha permissão apropriada para executar o comando que cria a saída, se você canalizar a saída do seu comando para tee, você só precisará elevar os privilégios do tee com sudo e direcionar o tee para gravar (ou anexar) no arquivo em questão.

no exemplo dado na pergunta, isso significaria:

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

para mais alguns exemplos práticos:

# 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

Em cada um desses exemplos, você está pegando a saída de um comando não privilegiado e gravando em um arquivo que geralmente é gravável apenas pela raiz, que é a origem da sua pergunta.

É uma boa idéia fazê-lo dessa maneira, porque o comando que gera a saída não é executado com privilégios elevados. Parece que não importa aqui, echomas quando o comando source é um script no qual você não confia completamente, é crucial.

Observe que você pode usar a opção -a para adicionar anexar (como >>) ao arquivo de destino em vez de substituí-lo (como >).

jg3
fonte
Js3 Desculpe, mas isso já foi sugerido (em 2008) e está sentado à resposta segunda maior: stackoverflow.com/a/82553/6910
Jonathan
2
Você está certo, Jonathan, atualizarei minha resposta para expandir as razões pelas quais essa é uma opção preferível. Obrigado pelo feedback útil.
Jg3
17

Faça o sudo executar um shell, assim:

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

A maneira como eu abordaria esse problema é:

Se você precisar gravar / substituir o arquivo:

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

Se você precisar anexar ao arquivo:

echo "some text" | sudo tee -a /path/to/file
Nikola Petkanski
fonte
1
Como isso é substancialmente diferente das respostas acima?
24616 Jonathan
2
Não é "substancialmente diferente", mas esclarece o uso distinto entre substituir e anexar ao arquivo.
Jamadagni
1
Esta é a resposta que copiei para minha folha de dicas.
MortimerCat
5

Que tal escrever um script?

Nome do arquivo: myscript

#!/bin/sh

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

# end script

Em seguida, use sudo para executar o script:

sudo ./myscript

fonte
4

Sempre que tenho que fazer algo assim, eu me torno raiz:

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

Provavelmente não é o melhor caminho, mas funciona.

Adam J. Forster
fonte
4

Eu faria assim:

sudo su -c 'ls -hal /root/ > /root/test.out'
fardjad
fonte
2
Isso realmente parece um pouco mais limpo, pois você não precisa especificar explicitamente o shell.
Steve Bennett
1
Um pequena desvantagem é que ele é executado um processo adicional ( 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

Não pretenda vencer um cavalo morto, mas há muitas respostas aqui tee, o que significa que você precisa redirecionar stdoutpara a /dev/nullmenos que queira ver uma cópia na tela.

Uma solução mais simples é usar apenas catassim:

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

Observe como o redirecionamento é colocado entre aspas para que ele seja avaliado por um shell iniciado em sudovez daquele que o executa.

haridsv
fonte
Isso funciona muito bem. Não entendo por que teve um voto negativo. Votado.
Teemu Leisti
4
Eu acho que não ganha muito amor porque não é muito melhor do que isso sudo bash -c "ls -hal /root > /root/test.out". Usar tee evita a necessidade de uma concha, enquanto o gato não.
Nick Russo
2

Isso é baseado na resposta que envolve tee. Para facilitar as coisas, escrevi um pequeno script (eu o chamo suwrite) e o coloco /usr/local/bin/com +xpermissão:

#! /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

Conforme mostrado em USAGE no código, tudo o que você precisa fazer é canalizar a saída para esse script, seguido pelo nome de arquivo acessível ao superusuário desejado e solicitará sua senha automaticamente, se necessário (uma vez que inclui sudo).

echo test | suwrite /root/test.txt

Observe que, como esse é um invólucro simples tee, ele também aceitará a -aopção do tee de acrescentar e também suporta a gravação em vários arquivos ao mesmo tempo.

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

Ele também possui alguma proteção simplista contra a gravação em /dev/dispositivos, o que foi uma preocupação mencionada em um dos comentários nesta página.

jamadagni
fonte
1

Talvez você tenha acesso ao sudo apenas a alguns programas / caminhos? Então não há como fazer o que você quer. (a menos que você o corte de alguma forma)

Se não for o caso, talvez você possa escrever o script bash:

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

Pressione ctrl+ d:

chmod a+x myscript.sh
sudo myscript.sh

Espero que ajude.

user15453
fonte
1
sudo at now  
at> echo test > /tmp/test.out  
at> <EOT>  
job 1 at Thu Sep 21 10:49:00 2017  
user8648126
fonte
1
Se você tiver de sudoqualquer maneira, atnão é útil ou necessário. sudo sh -c 'echo test >/tmp/test.out'faz o mesmo de maneira muito mais eficiente e elegante (mas ainda sofre com a falha de que você provavelmente está executando coisas como as rootquais não precisam desse privilégio; você geralmente deve evitar comandos privilegiados quando puder).
tripleee