Warum gibt Sudo Cat eine verweigerte Erlaubnis, aber Sudo Vim funktioniert einwandfrei?

84

Ich versuche, das Hinzufügen einer Repository-Quelle in der pacman.conf-Datei meines Arch zu automatisieren, verwende jedoch den echoBefehl in meinem Shell-Skript. Es schlägt jedoch folgendermaßen fehl: -

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

Wenn ich manuell Änderungen an /etc/pacman.conf mit vim vornehme, tun Sie dies

sudo vim /etc/pacman.conf

Wenn Sie vim mit beenden :wq, funktioniert alles einwandfrei und meine pacman.conf wurde manuell aktualisiert, ohne dass Beschwerden über "Berechtigung verweigert" vorliegen.

Warum ist das so? Und wie komme ich sudo echozur Arbeit? (Übrigens habe ich es auch versucht, sudo cataber das ist fehlgeschlagen, da auch die Berechtigung verweigert wurde.)

Calvin Cheng
quelle
3
Mögliches Duplikat von Wie verwende ich sudo, um die Ausgabe an einen Ort umzuleiten, an den ich keine Schreibberechtigung habe?
Ciro Santilli 法轮功 冠状 病 六四 事件 28

Antworten:

49

Das Problem ist, dass die Umleitung von Ihrer ursprünglichen Shell verarbeitet wird, nicht von sudo. Muscheln sind nicht in der Lage, Gedanken zu lesen und wissen nicht, dass diese bestimmte >>für die sudound nicht für sie bestimmt ist.

Du brauchst:

  1. zitieren Sie die Umleitung (so wird sie an weitergeleitet sudo)
  2. und verwenden sudo -s(so dass sudoeine Shell verwendet wird, um die angegebene Umleitung zu verarbeiten.)
Geekosaurier
quelle
1
Wenn Sie dies also in zwei Schritten wie sudo -sfolgt tun: (1) (2) Echo "# test" >> /etc/pacman.conf funktioniert. Aber ist es möglich, dies in einer einzigen Zeile auszuführen?
Calvin Cheng
15
sudo -s 'echo "# test" >>/etc/pacman.conf'ist das, was ich dir vermitteln wollte.
Geekosaurier
2
Eigentlich habe ich das versucht, nachdem ich Ihre Antwort oben gelesen habe, aber einen seltsamen Fehler wie diesen erhalten: - sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directoryDeshalb habe ich anschließend den 2-stufigen manuellen Prozess ausprobiert.
Calvin Cheng
16
Ah ..... ein letzter Versuch auf diese Weise hat funktioniert -echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
Calvin Cheng
1
Wie / wo erfahre ich von der sudoDeaktivierung der Konfiguration -s? Visudo?
Calvin Cheng
126

Wie @geekosaur erklärt hat, führt die Shell die Umleitung durch, bevor der Befehl ausgeführt wird. Wenn Sie dies eingeben:

sudo foo >/some/file

Ihr aktueller Shell-Prozess erstellt eine Kopie von sich selbst, die zuerst /some/filezum Schreiben geöffnet werden soll , dann den Dateideskriptor zur Standardausgabe macht und erst dann ausgeführt wird sudo.

Wenn Sie erlaubt sind (Sudoer-Konfigurationen schließen häufig das Ausführen von Shells aus), können Sie Folgendes tun:

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

Aber ich finde eine gute Lösung im Allgemeinen, | sudo teeanstelle von >und | sudo tee -aanstelle von zu verwenden >>. Das ist besonders nützlich, wenn die Umleitung der einzige Grund ist, den ich überhaupt brauche sudo. Schließlich wurde unnötig Prozesse als Root ausgeführt sudo, um dies zu vermeiden. Und echoals Root zu laufen ist einfach albern.

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

Ich habe > /dev/nullam Ende hinzugefügt, weil teedie Ausgabe sowohl an die angegebene Datei als auch an die eigene Standardausgabe gesendet wird und ich sie nicht auf meinem Terminal sehen muss. (Der teeBefehl verhält sich wie ein „T“ Verbinder in einer physikalischen Pipeline, das ist , wo es seinen Namen hat.) Und ich wechselte zu Apostrophe ( '... ') statt Doppelzimmer ( "... ") , so dass alles wörtlich und ich musste keinen Backslash vor den $In setzen $arch. (Ohne die Anführungszeichen oder den umgekehrten Schrägstrich $archwürde er durch den Wert des Shell-Parameters ersetzt arch, der wahrscheinlich nicht vorhanden ist. In diesem Fall wird der $archdurch nichts ersetzt und verschwindet einfach.)

Das kümmert sich also darum, mit root in Dateien zu schreiben sudo. Nun zu einem ausführlichen Exkurs über die Ausgabe von Zeilenumbruch enthaltendem Text in einem Shell-Skript. :) :)

Für BLUF wäre es, wie sie sagen, meine bevorzugte Lösung, einfach ein Here-Dokument in den obigen sudo teeBefehl einzugeben . dann besteht überhaupt keine Notwendigkeit für catoder echooder printfoder irgendwelche anderen Befehle. Die einfachen Anführungszeichen wurden in die Sentinel-Einführung verschoben, <<'EOF'haben dort jedoch den gleichen Effekt: Der Text wird als wörtlicher Text behandelt und $archbleibt daher in Ruhe:

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

EOF

Aber während ich es so machen würde, gibt es Alternativen. Hier sind ein paar:

Sie können bei einer echopro Zeile bleiben , aber alle in einer Unterschale zusammenfassen, sodass Sie die Datei nur einmal anhängen müssen:

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

Wenn Sie hinzufügen , -eum die echo(und Sie verwenden eine Shell , dass Träger , die nicht-POSIX - Erweiterung), können Sie neue Zeilen direkt in die Zeichenfolge einbetten kann mit \n:

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

Wie oben erwähnt, handelt es sich jedoch nicht um ein von POSIX angegebenes Verhalten. Ihre Shell gibt möglicherweise nur ein Literal wieder, -egefolgt von einer Zeichenfolge mit einer Reihe von Literalen \n. Die POSIX-Methode besteht darin, printfanstelle von zu verwenden echo. Es behandelt sein Argument automatisch wie echo -efolgt, fügt jedoch am Ende nicht automatisch eine neue Zeile hinzu, sodass Sie dort auch ein zusätzliches hinzufügen müssen \n:

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

Bei beiden Lösungen enthält das, was der Befehl als Argumentzeichenfolge erhält \n, die zweistellige Folge , und es liegt am Befehlsprogramm selbst (dem Code in printfoder echo), dies in eine neue Zeile zu übersetzen. In vielen modernen Shells haben Sie die Möglichkeit, ANSI-Anführungszeichen $'... zu verwenden ', die Sequenzen wie \nin wörtliche Zeilenumbrüche übersetzen, bevor das Befehlsprogramm die Zeichenfolge jemals sieht. Das bedeutet , dass solche Strings arbeiten mit jedem Befehl auch immer, einschließlich plain old -e-weniger echo:

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

echo -eANSI-Anführungszeichen sind zwar portabler als , aber dennoch eine Nicht-POSIX-Erweiterung.

Und wieder, obwohl dies alles Optionen sind, bevorzuge ich die tee <<EOFobige direkte Lösung.

Mark Reed
quelle
Interessant! Könnten Sie einen Hinweis hinzufügen, in dem der Grund für das Aufteilen der Datei auf / dev / null erläutert wird?
Stricken
sudo bash -c 'foo >/some/file'funktioniert für mich auf osx :-)
kayochin
Ich bevorzuge diese Antwort, weil sie mit mehrzeiligem Text funktioniert. cat <<EOF | sudo tee -a /some/file > /dev/null ...
16.
Das ist praktisch meine letzte Antwort, außer dass Sie einen fremden catProzess hinzugefügt haben. :)
Mark Reed
17

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

Da die Anweisungen oben nicht so klar sind, verwende ich die Anweisungen aus diesem Blog-Beitrag. Anhand von Beispielen ist es einfacher zu erkennen, was Sie tun müssen.

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash: /root/example.gz: Berechtigung verweigert

Beachten Sie, dass es der zweite Befehl (der Befehl gzip) in der Pipeline ist, der den Fehler verursacht. Hier kommt unsere Technik ins Spiel, Bash mit der Option -c zu verwenden.

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

In der Ausgabe des Befehls ls können wir sehen, dass die Erstellung der komprimierten Datei erfolgreich war.

Die zweite Methode ähnelt der ersten, da wir eine Befehlszeichenfolge an bash übergeben, dies jedoch in einer Pipeline über 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

Nelaaro
quelle
1
Vielen Dank! Ihre Beispiele haben geholfen!
Dharmatech
1
Für einen so einfachen Befehl ist es möglicherweise etwas besser, sh anstelle von bash zu verwenden. ZB sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz '. Aber sonst gute Erklärungen, +1.
Bryce
7
sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'
Peyman Mahdian
quelle
1

SCHRITT 1 Erstellen Sie eine Funktion in einer Bash-Datei ( 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'wird keine $archVariable interpretieren .

STE2- Quell-Bash-Datei

$ source write_pacman.sh

SCHRITT 3 Funktion ausführen

$ write_pacman
betenagupd
quelle
Wofür ist das Zitat auf EOF?
Bilabila