Dlaczego sudo cat odmawia uprawnień, ale sudo vim działa dobrze?

86

Próbuję zautomatyzować dodawanie źródła repozytorium w pliku pacman.conf mojego arch, ale używając echopolecenia w moim skrypcie powłoki. Jednak kończy się to niepowodzeniem: -

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

Jeśli wprowadzę zmiany w /etc/pacman.conf ręcznie za pomocą vima, wykonując

sudo vim /etc/pacman.conf

i wychodząc z vima :wq, wszystko działa dobrze, a mój pacman.conf został ręcznie zaktualizowany bez narzekań „Odmowa uprawnień”.

Dlaczego tak jest? A jak dostanę się sudo echodo pracy? (btw, próbowałem sudo catteż użyć, ale to się nie udało z odmową uprawnień)

Calvin Cheng
źródło
3
możliwy duplikat Jak użyć sudo do przekierowania wyjścia do lokalizacji, do której nie mam uprawnień do zapisu?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

51

Problem polega na tym, że przekierowanie jest przetwarzane przez oryginalną powłokę, a nie przez sudo. Muszle nie są w stanie czytać w myślach i nie wiedzą, że ten konkretny >>jest przeznaczony dla niego, sudoa nie dla niego.

Musisz:

  1. zacytować przekierowanie (więc jest ono przekazywane do sudo)
  2. i użyj sudo -s(aby sudoużyć powłoki do przetworzenia cytowanego przekierowania).
geekozaur
źródło
1
Robiąc to w dwóch krokach, takich jak to (1) sudo -s(2), echo "# test" >> /etc/pacman.conf działa. Ale czy można to wykonać w jednej linii?
Calvin Cheng,
15
sudo -s 'echo "# test" >>/etc/pacman.conf'jest tym, co starałem się wam przekazać.
geekosaur
2
Właściwie spróbowałem tego po przeczytaniu twojej odpowiedzi powyżej, ale otrzymałem taki dziwny błąd: - sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directorydlatego później wypróbowałem dwuetapowy proces ręczny.
Calvin Cheng,
16
Ach ..... ostatnia taka próba zadziałała -echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
Calvin Cheng,
1
jak / gdzie znajdę informacje o sudowyłączaniu konfiguracji -s? visudo?
Calvin Cheng,
127

Jak wyjaśnił @geekosaur, powłoka dokonuje przekierowania przed uruchomieniem polecenia. Kiedy wpiszesz to:

sudo foo >/some/file

Twój bieżący proces powłoki tworzy kopię samego siebie, która najpierw próbuje otworzyć się /some/filedo zapisu, następnie ustawia ten deskryptor pliku jako standardowe wyjście, a dopiero potem wykonuje sudo.

Jeśli masz pozwolenie (konfiguracje sudoer często uniemożliwiają uruchamianie powłok), możesz zrobić coś takiego:

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

Ale ogólnie uważam, że dobrym rozwiązaniem jest użycie | sudo teezamiast >i | sudo tee -azamiast >>. Jest to szczególnie przydatne, jeśli przekierowanie jest jedynym powodem, którego potrzebuję sudow pierwszej kolejności; w końcu niepotrzebne uruchamianie procesów jako root jest właśnie tym, czego sudonależy unikać. A bieganie echojako root jest po prostu głupie.

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

Dodałem > /dev/nullna końcu, ponieważ teewysyła swoje dane wyjściowe zarówno do wymienionego pliku, jak i na swoje własne standardowe wyjście, i nie muszę go widzieć na moim terminalu. ( teePolecenie działa jak łącznik "T" w fizycznym potoku, skąd bierze swoją nazwę.) I przełączyłem się na pojedyncze cudzysłowy ( '... ') zamiast podwójnych ( "... "), więc wszystko jest dosłowne i ja nie trzeba było umieszczać odwrotnego ukośnika przed $in $arch. (Bez cudzysłowów lub ukośnika odwrotnego $archzostałby zastąpiony wartością parametru powłoki arch, który prawdopodobnie nie istnieje, w takim przypadku $archjest zastępowany przez nic i po prostu znika).

To zajmuje się zapisywaniem do plików jako root przy użyciu sudo. Teraz długa dygresja na temat sposobów wyprowadzania tekstu zawierającego znak nowej linii w skrypcie powłoki. :)

Dla BLUF, jak mówią, moim preferowanym rozwiązaniem byłoby po prostu wprowadzenie dokumentu tutaj do powyższego sudo teepolecenia; wtedy nie ma potrzeby catlub echolub printflub innych komend w ogóle. Pojedyncze cudzysłowy przeniosły się do wprowadzenia wartowniczego <<'EOF', ale tam mają ten sam efekt: treść jest traktowana jako tekst dosłowny, więc $archpozostaje w spokoju:

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

EOF

Ale chociaż tak bym to zrobił, są alternatywy. Tu jest kilka:

Możesz trzymać się jednego w echokażdym wierszu, ale zgrupować je wszystkie razem w podpowłoce, więc wystarczy dołączyć do pliku tylko raz:

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

Jeśli dodasz -edo echo(i używasz powłoki, która obsługuje to rozszerzenie inne niż POSIX), możesz osadzić znaki nowej linii bezpośrednio w ciągu za pomocą \n:

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

Ale jak powiedziano powyżej, nie jest to zachowanie określone w standardzie POSIX; Twoja powłoka może zamiast tego powtórzyć literał, -ea po nim ciąg znaków zawierający kilka literałów \ns. Sposób zgodny z POSIX polega na używaniu printfzamiast echo; automatycznie traktuje swój argument tak jak echo -erobi, ale nie dodaje automatycznie nowej linii na końcu, więc musisz też wstawić \ntam dodatkowy :

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

W przypadku każdego z tych rozwiązań to, co polecenie otrzymuje jako ciąg argumentu, zawiera sekwencję dwóch znaków \n, a sam program poleceń (kod wewnątrz printflub echo) musi przetłumaczyć to na znak nowej linii. W wielu współczesnych muszli, masz możliwość korzystania cytaty ANSI $'... ', co przełoży sekwencje jak \njęzyk dosłownych nowej linii zanim program komenda kiedykolwiek widzi ciąg. Oznacza to, że takie ciągi działają z każdym poleceniem, w tym zwykłym starym- -ebez echo:

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

Ale, choć bardziej przenośne niż echo -e, cudzysłowy ANSI nadal są rozszerzeniem innym niż POSIX.

I znowu, chociaż to wszystkie opcje, wolę proste tee <<EOFrozwiązanie powyżej.

Mark Reed
źródło
Ciekawy! Czy możesz dodać notatkę wyjaśniającą powód podzielenia pliku na / dev / null?
dzianiny
sudo bash -c 'foo >/some/file'działa u mnie na osx :-)
kayochin
Wolę tę odpowiedź, ponieważ działa z tekstem wielowierszowym. cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan
To moja ostatnia odpowiedź, z wyjątkiem tego, że dodałeś dodatkowy catproces. :)
Mark Reed
17

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

Ponieważ powyższe instrukcje nie są zbyt jasne, korzystam z instrukcji z tego wpisu na blogu. Z przykładami, aby łatwiej było zobaczyć, co musisz zrobić.

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash: /root/example.gz: Odmowa uprawnień

Zwróć uwagę, że to drugie polecenie (polecenie gzip) w potoku powoduje błąd. W tym miejscu pojawia się nasza technika używania basha z opcją -c.

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

Na podstawie danych wyjściowych polecenia ls widzimy, że utworzenie skompresowanego pliku powiodło się.

Druga metoda jest podobna do pierwszej, ponieważ przekazujemy polecenie do bash, ale robimy to w potoku za pośrednictwem 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
źródło
1
W przypadku tak prostego polecenia może być nieco lepiej użyć sh zamiast bash. Np. Sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz '. Ale poza tym dobre wyjaśnienia, +1.
Bryce
7
sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'
Peyman Mahdian
źródło
1

KROK 1 utwórz funkcję w pliku 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'nie będzie interpretować $archzmiennej.

Plik źródłowy bash STE2

$ source write_pacman.sh

KROK 3 wykonaj funkcję

$ write_pacman
prayagupd
źródło
po co jest wycena na EOF?
bilabila
0

dołącz pliki (sudo cat):

cat <origin-file> | sudo tee -a <target-file>

dołącz echo do pliku (sudo echo):

echo <origin> | sudo tee -a <target-file>

(EXTRA) pomijają wynik:

echo >origin> | sudo tee -a <target-file> >/dev/null
diogowalterdefreitas
źródło