Comment utiliser sudo pour rediriger la sortie vers un emplacement où je n'ai pas l'autorisation d'écrire?

881

J'ai reçu un accès sudo sur l'une de nos boîtiers de développement RedHat linux, et il me semble que j'ai souvent besoin de rediriger la sortie vers un emplacement auquel je n'ai normalement pas accès en écriture.

Le problème est que cet exemple artificiel ne fonctionne pas:

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

Je viens de recevoir la réponse:

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

Comment puis-je faire en sorte que cela fonctionne?

Jonathan
la source
1
use chmod u + w filename
Szabolcs Dombi
@DombiSzabolcs Vous proposez que je crée d'abord le fichier en tant que sudo, puis me donne la permission? Bonne idée.
Jonathan
Dans de nombreuses situations, vous vous retrouvez ici parce que vous avez demandé "pourquoi la permission est-elle refusée?" Parfois, la réponse est que vous devez créer un fichier en tant que root(dans ce cas, procédez à la lecture des réponses) mais très souvent, vous devez simplement créer le fichier ailleurs, comme vous-même.
tripleee
1
Après avoir lutté avec ces réponses, j'ai finalement choisi de rediriger vers un fichier temporaire et de le déplacer vers la destination.
TingQian LI
Voir aussi stackoverflow.com/questions/84882/…
Nate Eldredge

Réponses:

1233

Votre commande ne fonctionne pas car la redirection est effectuée par votre shell qui n'a pas l'autorisation d'écrire /root/test.out. La redirection de la sortie n'est pas effectuée par sudo.

Il existe plusieurs solutions:

  • Exécutez un shell avec sudo et donnez-lui la commande en utilisant l' -coption:

    sudo sh -c 'ls -hal /root/ > /root/test.out'
    
  • Créez un script avec vos commandes et exécutez ce script avec sudo:

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

    Courez sudo ls.sh. Voir la réponse de Steve Bennett si vous ne souhaitez pas créer de fichier temporaire.

  • Lancez un shell avec sudo -spuis exécutez vos commandes:

    [nobody@so]$ sudo -s
    [root@so]# ls -hal /root/ > /root/test.out
    [root@so]# ^D
    [nobody@so]$
    
  • Utilisez sudo tee(si vous devez beaucoup vous échapper lorsque vous utilisez l' -coption):

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

    La redirection vers /dev/nullest nécessaire pour empêcher la sortie de tee sur l'écran. Pour ajouter au lieu d'écraser le fichier de sortie ( >>), utilisez tee -aou tee --append(le dernier est spécifique aux coreutils GNU ).

Merci à Jd , Adam J. Forster et Johnathan pour les deuxième, troisième et quatrième solutions.

Cristian Ciupitu
la source
1
Il y a une excellente réponse qui vous indique comment rediriger STDERR et STDOUT séparément ici: stackoverflow.com/questions/692000/… ... en grosperl -e 'print "STDIN\n"; print STDERR "STDERR\n"; ' > >( tee stdout.log ) 2> >( tee stderr.log >&2 )
errant.info
2
Vous aurez envie de faire 'sudo -E ...' afin d'utiliser des variables dans la commande shelled out (lors de l'utilisation dans un script, par exemple).
Urhixidur
3
La redirection de la sortie tee vers / dev / null n'est probablement pas nécessaire dans de nombreux cas où l'écho de la sortie vers l'écran est sans danger. Par exemple, lorsqu'il s'agit uniquement de la sortie de commandes régulières ou du contenu de petits fichiers texte.
thomasrutter
105

Quelqu'un ici vient de suggérer un tee-shirt sudoing:

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

Cela peut également être utilisé pour rediriger n'importe quelle commande vers un répertoire auquel vous n'avez pas accès. Cela fonctionne parce que le programme tee est en fait un programme "écho vers un fichier", et la redirection vers / dev / null est de l'empêcher de sortir également à l'écran pour le garder comme l'exemple original créé ci-dessus.

Jonathan
la source
5
Dans de nombreux cas, à savoir si l'utilisateur normal a la permission d'exécuter la commande et "seulement" ne peut pas écrire dans le fichier de sortie souhaité, le premier sudo(c'est-à-dire pour la commande elle-même) peut être omis
Hagen von Eitzen
81

Un truc que j'ai compris moi-même était

sudo ls -hal /root/ | sudo dd of=/root/test.out
rhlee
la source
16
sudo ddest meilleur que les sudo tee /root/file > /dev/nullexemples ci-dessus!
kristianlm
14
dd n'est pas une commande obscure bizarre. Il est utilisé chaque fois que vous avez besoin de copier de grandes quantités de données avec mise en mémoire tampon entre deux périphériques de bloc. La syntaxe est en fait assez simple, ddc'est le nom de la commande, of=/root/test.outc'est l'argument qui indique ddce que sont les fichiers de sortie.
rhlee
14
@steve Tout est «obscur» jusqu'à ce que vous appreniez ce que c'est. ofsignifie fichier de sortie et ddest un outil très populaire utilisé à la fois sous Linux et OSX (principalement pour écrire des images sur des disques). C'est une astuce intéressante à coup sûr.
blockloop
12
ddest probablement aussi utile teequ'ici. Dans les deux cas, vous utilisez une commande courante et bien connue dans un but qui, bien que légèrement différent de son objectif initial, est toujours bien connu et bien documenté. Bien qu'il ddsoit bon pour copier d'énormes quantités de données, il n'aspire pas avec de petites quantités. Il présente ici l'avantage de ne pas faire écho de la sortie à la sortie standard également.
thomasrutter
26
Chaque fois que vous tapez des mots sudo ddles uns à côté des autres, vous voulez être très, très sûr que les arguments qui suivent sont corrects (surtout compte tenu de sa syntaxe non standard). Ils ne l'appellent pas "destructeur de disque" pour rien ...
ali_m
45

Le problème est que la commande est exécutée sous sudo, mais la redirection est exécutée sous votre utilisateur. Cela se fait par le shell et il n'y a pas grand chose à faire.

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

Les moyens habituels de contourner cela sont:

  • Enveloppez les commandes dans un script que vous appelez sous sudo.

    Si les commandes et / ou le fichier journal changent, vous pouvez faire en sorte que le script les prenne comme arguments. Par exemple:

    sudo log_script command /log/file.txt
    
  • Appelez un shell et passez la ligne de commande comme paramètre avec -c

    Ceci est particulièrement utile pour les commandes composées uniques. Par exemple:

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

Encore une autre variation sur le thème:

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

Ou bien sûr:

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

Ils ont l'avantage (minuscule) que vous n'avez pas besoin de vous souvenir des arguments sudoou sh/bash

Steve Bennett
la source
18

Clarifier un peu pourquoi l'option tee est préférable

En supposant que vous disposez des autorisations appropriées pour exécuter la commande qui crée la sortie, si vous dirigez la sortie de votre commande vers tee, vous n'avez qu'à élever les privilèges de tee avec sudo et demander à tee d'écrire (ou d'ajouter) au fichier en question.

dans l'exemple donné dans la question, cela signifierait:

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

pour quelques exemples plus pratiques:

# 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

Dans chacun de ces exemples, vous prenez la sortie d'une commande non privilégiée et écrivez dans un fichier qui n'est généralement accessible en écriture que par racine, ce qui est à l'origine de votre question.

C'est une bonne idée de procéder de cette façon car la commande qui génère la sortie n'est pas exécutée avec des privilèges élevés. Cela ne semble pas avoir d'importance ici, echomais lorsque la commande source est un script auquel vous ne faites pas entièrement confiance, c'est crucial.

Notez que vous pouvez utiliser l'option -a pour té pour ajouter ajouter (comme >>) au fichier cible plutôt que de l'écraser (comme >).

jg3
la source
Désolé js3, mais cela a déjà été suggéré (en 2008) et se trouve à la deuxième réponse la plus élevée: stackoverflow.com/a/82553/6910
Jonathan
2
Vous avez raison, Jonathan, je mettrai à jour ma réponse pour développer les raisons pour lesquelles c'est une option préférable. Merci pour les commentaires utiles.
jg3
17

Faites exécuter sudo un shell, comme ceci:

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

La façon dont je voudrais aborder cette question est la suivante:

Si vous devez écrire / remplacer le fichier:

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

Si vous devez ajouter au fichier:

echo "some text" | sudo tee -a /path/to/file
Nikola Petkanski
la source
1
En quoi est-ce substantiellement différent des réponses ci-dessus?
Jonathan
2
Ce n'est pas "substantiellement différent" mais cela clarifie l'usage distinctif entre le remplacement et l'ajout au fichier.
jamadagni
1
C'est la réponse que j'ai copiée sur ma feuille de triche.
MortimerCat
5

Que diriez-vous d'écrire un script?

Nom de fichier: myscript

#!/bin/sh

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

# end script

Utilisez ensuite sudo pour exécuter le script:

sudo ./myscript

la source
4

Chaque fois que je dois faire quelque chose comme ça, je deviens simplement root:

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

Ce n'est probablement pas la meilleure façon, mais cela fonctionne.

Adam J. Forster
la source
4

Je le ferais de cette façon:

sudo su -c 'ls -hal /root/ > /root/test.out'
fardjad
la source
2
Cela semble en fait un peu plus propre, car vous n'avez pas besoin de spécifier explicitement le shell.
Steve Bennett
1
Un petit inconvénient est qu'il exécute un processus supplémentaire ( 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

Ne pas vouloir battre un cheval mort, mais il y a trop de réponses ici qui utilisent tee, ce qui signifie que vous devez rediriger stdoutvers, /dev/nullsauf si vous voulez voir une copie à l'écran.

Une solution plus simple consiste à simplement utiliser catcomme ceci:

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

Remarquez comment la redirection est placée entre guillemets afin qu'elle soit évaluée par un shell démarré par sudoau lieu de celui qui l'exécute.

haridsv
la source
Cela fonctionne très bien. Je ne comprends pas pourquoi il y a eu un vote négatif. A voté.
Teemu Leisti
4
Je pense que ça n'a pas beaucoup d'amour parce que ce n'est pas beaucoup mieux que ça sudo bash -c "ls -hal /root > /root/test.out". L'utilisation de tee évite d'avoir besoin d'une coquille, contrairement au chat.
Nick Russo
2

Ceci est basé sur la réponse impliquant tee. Pour faciliter les choses, j'ai écrit un petit script (je l'appelle suwrite) et l' ai mis /usr/local/bin/avec la +xpermission:

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

Comme indiqué dans USAGE dans le code, tout ce que vous avez à faire est de diriger la sortie vers ce script suivi du nom de fichier accessible par le superutilisateur et il vous demandera automatiquement votre mot de passe si nécessaire (car il comprend sudo).

echo test | suwrite /root/test.txt

Notez que puisqu'il s'agit d'un simple wrapper pour tee, il acceptera également l' -aoption de tee à ajouter et prend également en charge l'écriture dans plusieurs fichiers en même temps.

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

Il dispose également d'une protection simpliste contre l'écriture sur les /dev/appareils, ce qui était une préoccupation mentionnée dans l'un des commentaires sur cette page.

jamadagni
la source
1

Peut-être que vous n'avez eu accès à sudo qu'à certains programmes / chemins? Ensuite, il n'y a aucun moyen de faire ce que vous voulez. (à moins que vous ne le piratiez d'une manière ou d'une autre)

Si ce n'est pas le cas, vous pouvez peut-être écrire un script bash:

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

Appuyez sur ctrl+ d:

chmod a+x myscript.sh
sudo myscript.sh

J'espère que cela vous aidera.

user15453
la source
1
sudo at now  
at> echo test > /tmp/test.out  
at> <EOT>  
job 1 at Thu Sep 21 10:49:00 2017  
user8648126
la source
1
Si vous en avez sudoquand même, ce atn'est pas utile ou nécessaire. sudo sh -c 'echo test >/tmp/test.out'fait la même chose de manière plus efficace et élégante (mais souffre toujours du défaut que vous exécutez probablement des choses rootqui n'ont pas besoin de ce privilège; vous devriez généralement éviter les commandes privilégiées lorsque vous le pouvez).
tripleee