95 votes

Pourquoi sudo cat donne une permission refusée mais sudo vim fonctionne bien ?

J'essaie d'automatiser l'ajout d'une source de dépôt dans le fichier pacman.conf de mon système d'archivage, mais en utilisant la méthode d'ajout d'une source de dépôt. echo dans mon shell script. Cependant, cela échoue comme ceci :-

sudo echo "[archlinuxfr]" >> /etc/pacman.conf
sudo echo "Server = http://repo.archlinux.fr/\$arch" >> /etc/pacman.conf
sudo echo " " >> /etc/pacman.conf

-bash: /etc/pacman.conf: Permission denied

Si j'apporte des modifications à /etc/pacman.conf manuellement en utilisant vim, en faisant

sudo vim /etc/pacman.conf

et quitter vim avec :wq Tout fonctionne bien et mon pacman.conf a été mis à jour manuellement sans plaintes "Permission refusée".

Pourquoi en est-il ainsi ? Et comment puis-je obtenir sudo echo pour fonctionner ? (au fait, j'ai essayé d'utiliser sudo cat aussi mais cela a échoué avec Permission refusée aussi)

141voto

Mark Reed Points 23817

Comme l'explique @geekosaur, le shell effectue la redirection avant d'exécuter la commande. Quand vous tapez ceci :

sudo foo >/some/file

Votre processus shell actuel fait une copie de lui-même qui essaie d'abord d'ouvrir /some/file pour l'écriture, puis si cela réussit, il fait de ce descripteur de fichier sa sortie standard, et seulement si cela réussit, il exécute sudo . C'est un échec dès la première étape.

Si vous y êtes autorisé (les configurations sudoer empêchent souvent l'exécution de shells), vous pouvez faire quelque chose comme ceci :

sudo bash -c 'foo >/some/file'

Mais je trouve qu'une bonne solution en général est d'utiliser | sudo tee au lieu de > y | sudo tee -a au lieu de >> . C'est particulièrement utile si la redirection est la seule raison pour laquelle j'ai besoin de sudo en premier lieu ; après tout, faire tourner inutilement des processus comme Root est précisément ce qui sudo a été créé pour éviter. Et courir echo comme Root est tout simplement stupide.

echo '[archlinuxfr]' | sudo tee -a /etc/pacman.conf >/dev/null
echo 'Server = http://repo.archlinux.fr/$arch' | sudo tee -a /etc/pacman.conf >/dev/null
echo ' ' | sudo tee -a /etc/pacman.conf >/dev/null

J'ai ajouté > /dev/null à la fin parce que tee envoie sa sortie à les deux le fichier nommé y sa propre sortie standard, et je n'ai pas besoin de la voir sur mon terminal. (Les tee agit comme un connecteur en "T" dans un pipeline physique, d'où son nom). Et je suis passé aux guillemets simples ( ' ... ' ) au lieu de doubles ( " ... " ) afin que tout soit littéral et que je n'aie pas à mettre une barre oblique inversée devant le nom de l'utilisateur. $ en $arch . (Sans les guillemets ni la barre oblique inversée, $arch serait remplacé par la valeur du paramètre shell arch qui n'existe probablement pas, auquel cas l'option $arch est remplacé par rien et disparaît tout simplement).

Cela permet de s'occuper de l'écriture dans les fichiers en tant que Root en utilisant sudo . Maintenant, une longue digression sur les moyens de produire du texte contenant des nouvelles lignes dans un shell script. :)

Afin de le mettre en valeur, comme on dit, ma solution préférée serait d'introduire un document ici dans la méthode ci-dessus. sudo tee Il n'est donc pas nécessaire d'utiliser la commande cat o echo o printf ou toute autre commande. Les guillemets simples se sont déplacés vers l'introduction de la sentinelle <<'EOF' mais ils ont le même effet ici : le corps est traité comme du texte littéral, donc $arch est laissé seul :

sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/$arch

EOF

Mais si c'est ainsi que je procéderais, il existe des alternatives. En voici quelques-unes :

Vous pouvez vous en tenir à un seul echo par ligne, mais regroupez-les toutes dans un sous-shell, de sorte que vous n'ayez à ajouter au fichier qu'une seule fois :

(echo '[archlinuxfr]'
 echo 'Server = http://repo.archlinux.fr/$arch'
 echo ' ') | sudo tee -a /etc/pacman.conf >/dev/null

Si vous ajoutez -e à la echo (et que vous utilisez un interpréteur de commandes qui prend en charge cette extension non-POSIX), vous pouvez intégrer les nouvelles lignes directement dans la chaîne de caractères en utilisant la commande \n :

# NON-POSIX - NOT RECOMMENDED
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Mais comme il est dit plus haut, ce n'est pas un comportement spécifié par POSIX ; votre shell pourrait simplement renvoyer un texte littéral -e suivi d'une chaîne de caractères contenant un tas de caractères littéraux \n à la place. La manière POSIX de faire cela est d'utiliser printf au lieu de echo ; il traite automatiquement son argument comme echo -e le fait, mais n'ajoute pas automatiquement une nouvelle ligne à la fin, vous devez donc ajouter une ligne supplémentaire à la fin de la ligne. \n là aussi :

printf '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n \n' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Avec l'une ou l'autre de ces solutions, ce que la commande obtient comme chaîne d'arguments contient la séquence de deux caractères \n et c'est au programme de commande lui-même (le code à l'intérieur de la commande) d'agir. printf o echo ) pour le traduire en une nouvelle ligne. Dans de nombreux shells modernes, vous avez la possibilité d'utiliser les guillemets ANSI $' ... ' qui traduira des séquences comme \n en littéral des retours à la ligne avant que le programme de commande ne voie la chaîne. Cela signifie que de telles chaînes de caractères fonctionnent avec n'importe quelle commande, y compris la bonne vieille -e -moins echo :

echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Mais, bien que plus portable que echo -e les guillemets ANSI sont toujours une extension non-POSIX.

Et encore une fois, bien que ce soient toutes des options, je préfère la ligne droite. tee <<EOF solution ci-dessus.

0 votes

Intéressant ! Pourriez-vous ajouter une note expliquant la raison pour laquelle le fichier a également été placé dans /dev/null ?

0 votes

sudo bash -c 'foo >/some/file' fonctionne pour moi sur osx :-)

0 votes

Je préfère cette réponse car elle fonctionne avec du texte multiligne. cat <<EOF | sudo tee -a /some/file > /dev/null ...

58voto

geekosaur Points 26170

Le problème est que la redirection est traitée par votre shell d'origine, et non par sudo . Les obus ne sont pas capables de lire dans les pensées et ne savent pas que ce particulier >> est destiné à la sudo et pas pour.

Vous en avez besoin :

  1. citer la redirection ( afin qu'elle soit transmise à sudo)
  2. y utiliser sudo -s (afin que sudo utilise un shell pour traiter la redirection citée).

1 votes

Il faut donc le faire en deux étapes comme ceci (1) sudo -s (2) echo "# test" >> /etc/pacman.conf fonctionne. Mais est-il possible d'exécuter ceci en une seule ligne ?

16 votes

sudo -s 'echo "# test" >>/etc/pacman.conf' c'est ce que j'essayais de vous faire comprendre.

2 votes

En fait, j'ai essayé cela après avoir lu votre réponse ci-dessus mais j'ai obtenu une erreur bizarre comme celle-ci :-. sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directory c'est pourquoi j'ai ensuite essayé le processus manuel en 2 étapes.

18voto

nelaar Points 593

http://www.innovationsts.com/blog/?p=2758

Comme les instructions ne sont pas très claires ci-dessus, j'utilise les instructions de cet article de blog. Avec des exemples, il est plus facile de voir ce que vous devez faire.

$ sudo cat /Root/example.txt | gzip > /Root/example.gz
-bash : /Root/example.gz : Permission refusée

Remarquez que c'est la deuxième commande (la commande gzip) dans le pipeline qui provoque l'erreur. C'est là que notre technique d'utilisation de bash avec l'option -c entre en jeu.

$ sudo bash -c 'cat /Root/example.txt | gzip > /Root/example.gz'
$ sudo ls /Root/example.gz
/Root/example.gz

Nous pouvons voir à partir de la sortie de la commande ls que la création du fichier compressé a réussi.

La deuxième méthode est similaire à la première dans la mesure où nous passons une chaîne de commande à bash, mais nous le faisons dans un pipeline via sudo.

$ sudo rm /Root/example.gz
$ echo "cat /Root/example.txt | gzip > /Root/example.gz" | sudo bash
$ sudo ls /Root/example.gz
/Root/example.gz

1 votes

Pour une commande aussi simple, il peut être légèrement préférable d'utiliser sh plutôt que bash. Par exemple, sudo sh -c 'cat /Root/example.txt | gzip > /Root/example.gz'. Mais sinon, bonnes explications, +1.

1voto

Prayag Upd Points 3347

ÉTAPE 1 créer une fonction dans un fichier bash ( write_pacman.sh )

#!/bin/bash

function write_pacman {
 tee -a /etc/pacman.conf > /dev/null << 'EOF'
  [archlinuxfr]
  Server = http://repo.archlinux.fr/\$arch
EOF
}

'EOF' n'interprétera pas $arch variable.

STE2 fichier source bash

$ source write_pacman.sh

ÉTAPE 3 exécuter la fonction

$ write_pacman

0 votes

A quoi sert la cotation sur EOF ?

0voto

Morg. Points 520

Une preuve supplémentaire si nécessaire que sudo n'est pas une si bonne idée après tout.

sudo su root -c "cd /"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X