Redirection de stdout vers un fichier sur lequel vous ne disposez pas de droits d'écriture

113

Lorsque vous essayez de modifier un fichier sans avoir les droits d'écriture, vous obtenez une erreur:

> touch /tmp/foo && sudo chown root /tmp/foo
> echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Sudoing n'aide pas, car il exécute la commande en tant que root, mais le shell gère la redirection de stdout et ouvre le fichier comme vous le faites quand même:

> sudo echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Existe-t-il un moyen simple de rediriger stdout vers un fichier sur lequel vous n’avez pas l’autorisation d’écrire, en plus d’ouvrir un shell en tant que root et de manipuler le fichier de cette façon?

> sudo su
# echo test > /tmp/foo
Michael Mrozek
la source
2
Réponse pour une question similaire de StackOverflow stackoverflow.com/questions/82256/…
Cristian Ciupitu
comment pouvez-vous ne pas avoir l'autorisation de créer un fichier que vous avez créé vous-même dans tmp? est-ce que c'est parce que d'umask?
k961
@ k961 J'avais l'habitude chownde changer de propriétaire; ce n'était qu'un exemple
Michael Mrozek

Réponses:

116

Oui, en utilisant tee. Ainsi echo test > /tmp/foodevient

echo test | sudo tee /tmp/foo

Vous pouvez aussi ajouter ( >>)

echo test | sudo tee -a /tmp/foo
Gert
la source
27
Tee sera également sorti sur stdout; Parfois, vous ne voulez pas que le contenu remplisse l'écran. Pour résoudre ce problème, faitesecho test | sudo tee /tmp/foo > /dev/null
Shawn J. Goff
Comment allez-vous le faire avec heredoc?
JohnDoea
26

Pour remplacer le contenu du fichier par la sortie de echo(comme l' >opérateur de redirection du shell).

echo test | sudo dd of=/tmp/foo

Pour écrire dans le fichier (au début, bien que vous puissiez utiliser seekpour générer des décalages différents) sans tronquer (comme l’ 1<>opérateur Bourne Shell):

echo test | sudo dd of=/tmp/foo conv=notrunc

Pour ajouter au fichier (comme >>), avec GNU dd:

echo test | sudo dd of=/tmp/foo oflag=append conv=notrunc

Voir aussi GNU ddde conv=exclfichier pour éviter un existant démolir (comme avec set -o noclobberdans les coquilles POSIX) et conv=nocreatle contraire (mise à jour uniquement un fichier existant).

Chris Jepeway
la source
intelligent! Cela évite d'avoir echo test | sudo tee /tmp/foo >/dev/nullà supprimer la sortie.
Adam Katz
2
Je devrais peut-être reprendre ça; ddn'est pas fiable pour cela, sauf si vous utilisez des options obscures réservées à GNU iflag=fullblock oflag=fullblock, qui enlèvent toute l'élégance de cette réponse. Je vais rester avec tee.
Adam Katz
5
dd est fiable avec le non-obscur bs = 1
umeboshi
2
@umeboshi Mais fiable seulement si vous êtes assez expérimenté pour savoir exactement ce que vous faites. Pour ddpeut être assez dangereux (sinon dire: dévastateur) si seulement une légère erreur a été commise. Donc, pour les nouveaux utilisateurs, je recommanderais plutôt la teeméthode sur la rive sûre.
erreur de syntaxe le
1
@AdamKatz, dans le cas de dd of=fileseul (sans count/ skip...), il est fiable. iflag=fullblockn'est pas nécessaire car ici ddécrit en sortie ce qu'il a lu en entrée. Peu importe que ce ne soit pas des blocs complets.
Stéphane Chazelas
12

tee est probablement le meilleur choix, mais selon votre situation, cela peut suffire:

sudo sh -c 'echo test > /tmp/foo'
phunehehe
la source
1
3 problèmes: evalau lieu d'un simple sh -c; -i, ce qui changera votre répertoire de travail; exécuter la commande entière en tant que root, ce qui pourrait changer son comportement ou présenter un risque inutile. pourquoi cela at-il déjà eu un vote positif?
4
vous ne l'avez pas fait, mais c'est trompeur, généralement mauvais et peut-être même dangereux.
5

Bien que je sois d’accord, c’est | sudo teela voie canonique, parfois sed (ici en supposant que GNU sed) peut fonctionner:

cat sudotest 
line 1

sudo sed -i '1iitest' sudotest && cat sudotest 
itest
line 1

sudo sed -i '$aatest' sudotest && cat sudotest 
itest
line 1
atest

-imodifie le fichier en place. 1isignifie insérer avant la ligne 1. $asignifie ajouter après la dernière ligne.

Ou copier sur xclipboard:

somecommand | xclip
sudo gedit sudotest
move cursor to desired place, click middle mouse button to insert, save
Utilisateur inconnu
la source
1
Ceci est libellé comme une énigme chinoise ancienne. Je ne vois pas pour comprendre comment cela a eu tant de votes positifs. ( hrmph )
erreur de syntaxe le
1
@ Syntaxerror: Monsieur, je suis désolé, je ne parle pas anglais. Si vous spécifiez ce qui n'est pas clair pour vous, je pourrais essayer d'améliorer ma réponse. Comparé à 65 votes positifs pour Gert, 4 ne semblent pas être beaucoup plus nombreux, non plus, vous ne pensez pas?
utilisateur inconnu le
Eh bien, je serais assez satisfait de 4 :-D Votre anglais n’est pas mauvais du tout. C'est juste écrit dans une sorte de nerdspeak technicien laconique. Je suppose que vous êtes un codeur. Les codeurs entre eux se comprendront parfaitement ainsi, mais un non-codeur doit penser que son libellé est le suivant: comme dit.
erreur de syntaxe le
Notez que sed -icela ne modifie pas réellement le fichier en place - il crée un fichier temporaire et le renomme à la sortie. Donc, vous ne pourrez pas faire quelque chose comme tail -f ...sur le fichier d'origine et voir la sortie en utilisant sed -i ...pendant que le pipeline est en cours d'exécution
Andrew Henle
@AndrewHenle: Oui, puisque la taille peut être augmentée ou réduite, et comme c'est probablement le cas pour la plupart des invocations sed, et que vous ne pouvez même pas - autant que je sache - écrire au même emplacement sur les SSD, il ne s'agit que d'une pseudo opération sur place . En tant que non anglophone, puis-je demander une brève expression, qui n'est pas si mal interprétée? Juste -i creates a new file of same nameou y a-t-il quelque chose de plus compact? Je suppose que j'aime bien in place, parce que cela explique la i. Le manpage appelle sed gnu- en place trop et long drapeau est --in-place.
utilisateur inconnu
2

J'avais des idées en tête à propos d'un problème similaire et j'ai proposé les solutions suivantes:

  • sudo uncatuncatest un programme qui lit l'entrée standard et l'écrit dans le fichier nommé sur la ligne de commande, mais je n'ai pas uncatencore écrit .

  • sudocatla variante de ce sudoeditque je n'ai pas encore écrit qui fait un nettoyeur sudo catou sudo uncat.

  • ou cette petite astuce de l'utilisation sudoeditavec un éditeur qui est un script shell

    #!/bin/sh
    # uncat
    cat > "$1"
    

    qui peut être invoqué en tant que ou |sudo ./uncat fileou | EDITOR=./uncat sudoeditmais qui a des effets secondaires intéressants.

Hildred
la source
catprend une liste de fichiers à con cat iner donc UNCAT devrait prendre une liste de fichiers à un con chat iner à. Il faudrait utiliser la magie pour décider combien mettre dans chaque fichier. Nom alternatif comprennent dog, to-file, redirect.
ctrl-alt-delor
Je ne peux pas penser à une raison pour laquelle je voudrais uncatquand j'ai tee.
Wildcard
1
Eh bien, teel’inconvénient trivial est qu’il écrit son stdin sur sa stdout - ce qui est atténué de manière triviale en redirigeant la stdout /dev/null. D'autres alternatives incluent dd of=/tmp/foo(mentionnées dans une autre réponse), qui écrit des informations d'état sur stderr, et cp /dev/stdin /tmp/foo.
Scott
1

Utilisez une éponge du paquet moreutils. Il a l'avantage de ne pas écrire sur stdout.

echo test | sudo sponge /tmp/foo

Utilisez l'option -a pour ajouter un fichier au lieu de l'écraser.

Hontvári Levente
la source
1
Je ne suis pas sûr de l'avantage de ne pas écrire sur les cadeaux stdout, mais vous pouvez simplement rediriger le résultat /dev/nulls'il s'agissait d'un problème
Michael Mrozek
1
Bien sûr, je peux rediriger vers / dev / null, mais la commande est plus facile à lire et à saisir sans redirection. L'avantage de ne pas écrire sur stdout est que mon terminal n'est pas rempli d'ordures.
Hontvári Levente
1
Je ne voudrais pas installer un paquet pour épargner une redirection, mais j'utilise régulièrement sponge, donc c'est déjà là.
Hontvári Levente
0

L'erreur provient de l'ordre dans lequel le shell fait les choses.

La redirection est gérée avant même que le shell ne s'exécute sudo. Elle est donc effectuée avec les autorisations de l'utilisateur avec lequel vous travaillez. Comme vous ne disposez pas des autorisations en écriture pour créer / tronquer la cible de la redirection, vous obtenez une permission deniederreur du shell.

La solution consiste à garantir que le fichier de sortie est créé sous l'identité qui vous a été attribuée par sudo, par exemple, avec tee:

$ generate_output | sudo tee target_file
Kusalananda
la source