Jak używać sudo do przekierowywania danych wyjściowych do lokalizacji, w której nie mam uprawnień do pisania?

881

Dano mi dostęp do sudo na jednym z naszych pakietów programistycznych RedHat i wydaje mi się, że dość często muszę przekierowywać dane wyjściowe do lokalizacji, do której normalnie nie mam dostępu do zapisu.

Problem w tym, że ten wymyślony przykład nie działa:

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

Właśnie otrzymałem odpowiedź:

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

Jak mogę to uruchomić?

Jonathan
źródło
1
użyj chmod u + w nazwa pliku
Szabolcs Dombi
@DombiSzabolcs Sugerujesz, że najpierw utworzę plik jako sudo, a następnie dam sobie pozwolenie? Dobry pomysł.
Jonathan
W wielu sytuacjach trafiasz tutaj, ponieważ pytasz „dlaczego odmawia mi się pozwolenia?” Czasami odpowiedź jest taka, że ​​musisz utworzyć plik jako root(w takim przypadku czytaj dalej odpowiedzi), ale bardzo często po prostu musisz utworzyć plik gdzieś indziej, jak sam.
tripleee
1
Po zmaganiu się z tymi odpowiedziami w końcu zdecydowałem się przekierować do pliku tymczasowego i sudo przenieść go do miejsca docelowego.
TingQian LI
Zobacz także stackoverflow.com/questions/84882/…
Nate Eldredge

Odpowiedzi:

1232

Twoje polecenie nie działa, ponieważ przekierowanie jest wykonywane przez twoją powłokę, która nie ma uprawnień do pisania /root/test.out. Przekierowanie wyjścia nie jest wykonywane przez sudo.

Istnieje wiele rozwiązań:

  • Uruchom powłokę z sudo i wydaj komendę, używając -copcji:

    sudo sh -c 'ls -hal /root/ > /root/test.out'
    
  • Utwórz skrypt za pomocą swoich poleceń i uruchom ten skrypt za pomocą sudo:

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

    Uruchom sudo ls.sh. Zobacz Steve Bennett odpowiedź , jeśli nie chcesz utworzyć pliku tymczasowego.

  • Uruchom powłokę, a sudo -snastępnie uruchom polecenia:

    [nobody@so]$ sudo -s
    [root@so]# ls -hal /root/ > /root/test.out
    [root@so]# ^D
    [nobody@so]$
    
  • Użyj sudo tee(jeśli musisz często uciekać podczas korzystania z -copcji):

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

    Przekierowanie do /dev/nulljest potrzebne, aby zatrzymać wyświetlanie tee na ekranie. Aby dołączyć zamiast zastąpić plik wyjściowy ( >>), użyj tee -alub tee --append(ostatni jest specyficzny dla coreutils GNU ).

Dziękujemy Jd , Adamowi J. Forsterowi i Johnathanowi za drugie, trzecie i czwarte rozwiązanie.

Cristian Ciupitu
źródło
1
Jest świetna odpowiedź, która mówi ci, jak przekierować STDERR i STDOUT osobno tutaj: stackoverflow.com/questions/692000/ ... ... w zasadzieperl -e 'print "STDIN\n"; print STDERR "STDERR\n"; ' > >( tee stdout.log ) 2> >( tee stderr.log >&2 )
errant.info
2
Będziesz chciał wykonać polecenie „sudo -E ...”, aby użyć zmiennych w poleceniu shelled out (na przykład podczas korzystania ze skryptu).
Urhixidur
3
Przekierowanie wyjścia tee do / dev / null prawdopodobnie nie jest konieczne w wielu przypadkach, w których echo wyjścia na ekran jest nieszkodliwe. Na przykład, gdy mamy do czynienia tylko z wyjściem zwykłych poleceń lub zawartością małych plików tekstowych.
thomasrutter
105

Ktoś tutaj zaproponował właśnie koszulkę sudoing:

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

Można to również wykorzystać do przekierowania dowolnego polecenia do katalogu, do którego nie masz dostępu. Działa, ponieważ program tee jest w rzeczywistości programem „echa do pliku”, a przekierowanie do / dev / null ma za zadanie zatrzymać wyświetlanie go również na ekranie, aby pozostało takie samo jak w oryginalnym wymyślonym powyżej przykładzie.

Jonathan
źródło
5
W wielu przypadkach, a mianowicie jeśli normalny użytkownik ma uprawnienia do wykonania polecenia i „tylko” nie może zapisać do żądanego pliku wyjściowego, pierwszy sudo(tj. Dla samego polecenia) może zostać pominięty
Hagen von Eitzen,
81

Sztuczka, którą sam sobie wymyśliłem

sudo ls -hal /root/ | sudo dd of=/root/test.out
rhlee
źródło
16
sudo ddjest lepszy niż sudo tee /root/file > /dev/nullpowyższe przykłady!
kristianlm
14
dd nie jest dziwnym, niejasnym poleceniem. Jest używany, gdy trzeba skopiować duże ilości danych z buforowaniem między dwoma urządzeniami blokowymi. Składnia jest w rzeczywistości dość prosta, ddto nazwa polecenia, of=/root/test.outargument, który mówi, ddco to są pliki wyjściowe.
rhlee
14
@steve Wszystko jest „niejasne”, dopóki nie dowiesz się, co to jest. ofoznacza plik wyjściowy i ddjest bardzo popularnym narzędziem używanym zarówno w systemie Linux, jak i OSX (głównie do zapisywania obrazów na dyskach). To na pewno fajna sztuczka.
blockloop
12
ddjest prawdopodobnie równie przydatny jak teetutaj. W obu przypadkach używasz wspólnego, dobrze znanego polecenia w celu, który - choć nieco różni się od pierwotnego zamierzonego celu - jest nadal dobrze znany i dobrze udokumentowany. Chociaż ddjest dobry w kopiowaniu dużych ilości danych, nie zasysa niewielkich ilości. Ma tę zaletę, że nie przypomina echa wyjścia również standardowego.
thomasrutter
26
Ilekroć wpisujesz słowa sudo ddobok siebie, chcesz być bardzo, bardzo pewien, że następujące argumenty są poprawne (szczególnie biorąc pod uwagę jego niestandardową składnię). Nie bez powodu nazywają to „niszczycielem dysków”
ali_m
45

Problem polega na tym, że polecenie zostaje uruchomione sudo, ale przekierowanie jest uruchamiane przez użytkownika. Robi to powłoka i niewiele można na to poradzić.

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

Zwykłe sposoby na obejście tego są:

  • Zawiń polecenia w skrypt, który wywołujesz w sudo.

    Jeśli komendy i / lub plik dziennika ulegną zmianie, skrypt może traktować je jako argument. Na przykład:

    sudo log_script command /log/file.txt
    
  • Wywołaj powłokę i przekaż wiersz poleceń jako parametr za pomocą -c

    Jest to szczególnie przydatne w przypadku jednorazowych poleceń złożonych. Na przykład:

    sudo bash -c "{ command1 arg; command2 arg; } > /log/file.txt"
    
dsm
źródło
23

Kolejna odmiana na temat:

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

Lub oczywiście:

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

Mają (małe) tę zaletę, że nie trzeba pamiętać żadnych argumentów sudolub sh/bash

Steve Bennett
źródło
18

Wyjaśniono nieco, dlaczego preferowana jest opcja tee

Zakładając, że masz odpowiednie uprawnienia do wykonania polecenia, które tworzy dane wyjściowe, jeśli potokujesz dane wyjściowe swojego polecenia do tee, wystarczy jedynie podnieść uprawnienia tee za pomocą sudo i skierować tee do zapisu (lub dołączenia) do danego pliku.

w przykładzie podanym w pytaniu oznaczałoby to:

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

kilka praktycznych przykładów:

# 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

W każdym z tych przykładów pobierasz dane wyjściowe nieuprzywilejowanego polecenia i zapisujesz do pliku, który zwykle jest zapisywalny tylko przez root, który jest źródłem twojego pytania.

Warto to zrobić w ten sposób, ponieważ polecenie generujące dane wyjściowe nie jest wykonywane z podwyższonymi uprawnieniami. Wydaje się, że nie ma to znaczenia, echoale gdy polecenie źródłowe jest skryptem, któremu nie całkowicie ufasz, jest to bardzo ważne.

Zauważ, że możesz użyć opcji -a, aby dodać lub dołączyć >>plik docelowy (jak ) do pliku docelowego, zamiast go zastąpić (jak >).

jg3
źródło
Przepraszam, js3, ale zostało to już zasugerowane (w 2008 r.) I siedzi na drugiej najwyższej odpowiedzi: stackoverflow.com/a/82553/6910
Jonathan
2
Masz rację, Jonathan, zaktualizuję moją odpowiedź, aby wyjaśnić powody, dla których jest to preferowana opcja. Dziękujemy za pomocne opinie.
jg3
17

Niech sudo uruchomi powłokę, tak jak to:

sudo sh -c "echo foo > ~root/out"
Penfold
źródło
11

Chciałbym poradzić sobie z tym problemem:

Jeśli musisz zapisać / zamienić plik:

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

Jeśli chcesz dołączyć do pliku:

echo "some text" | sudo tee -a /path/to/file
Nikola Petkanski
źródło
1
Czym różni się to znacznie od powyższych odpowiedzi?
Jonathan
2
Nie jest „zasadniczo inny”, ale wyjaśnia charakterystyczne użycie między zamianą a dołączaniem do pliku.
jamadagni
1
To jest odpowiedź, którą skopiowałem do ściągawki.
MortimerCat
5

Co powiesz na napisanie skryptu?

Nazwa pliku: myscript

#!/bin/sh

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

# end script

Następnie użyj sudo, aby uruchomić skrypt:

sudo ./myscript

źródło
4

Ilekroć muszę zrobić coś takiego, po prostu się rootuję:

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

To chyba nie najlepszy sposób, ale działa.

Adam J. Forster
źródło
4

Zrobiłbym to w ten sposób:

sudo su -c 'ls -hal /root/ > /root/test.out'
fardjad
źródło
2
To wydaje się nieco czystsze, ponieważ nie musisz jawnie określać powłoki.
Steve Bennett
1
Jedną niewielką wadą jest to, że uruchamia jeden dodatkowy proces ( 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

Nie znaczy pokonać martwego konia, ale nie ma zbyt wiele odpowiedzi tutaj zastosowanie tee, co oznacza, że trzeba przekierować stdoutdo /dev/nullchyba że chcesz zobaczyć kopię na ekranie.

Prostszym rozwiązaniem jest cattakie użycie :

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

Zwróć uwagę, w jaki sposób przekierowanie jest umieszczane w cudzysłowach, aby było oceniane przez powłokę uruchomioną przez sudozamiast uruchomionej.

haridsv
źródło
To działa dobrze. Nie rozumiem, dlaczego miał jeden negatywny głos. Pozytywne.
Teemu Leisti
4
Myślę, że nie ma dużo miłości, ponieważ nie jest dużo lepszy niż sudo bash -c "ls -hal /root > /root/test.out". Użycie koszulki pozwala uniknąć konieczności używania skorupy, podczas gdy kot nie.
Nick Russo,
2

Jest to oparte na zaangażowanej odpowiedzi tee. Aby to ułatwić, napisałem mały skrypt (nazywam go suwrite) i wstawiłem /usr/local/bin/za +xzgodą:

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

Jak pokazano w USAGE w kodzie, wszystko, co musisz zrobić, to przesłać dane wyjściowe do tego skryptu, a następnie żądanej nazwy pliku dostępnej dla superużytkownika, aw razie potrzeby automatycznie wyświetli monit o podanie hasła (ponieważ zawiera sudo).

echo test | suwrite /root/test.txt

Zauważ, że ponieważ jest to proste opakowanie tee, zaakceptuje również -aopcję dołączenia tee , a także obsługuje zapis do wielu plików jednocześnie.

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

Ma również uproszczoną ochronę przed zapisem na /dev/urządzeniach, co było problemem wspomnianym w jednym z komentarzy na tej stronie.

jamadagni
źródło
1

Może dostałeś sudo dostęp tylko do niektórych programów / ścieżek? W takim razie nie ma możliwości robienia tego, co chcesz. (chyba że jakoś go zhakujesz)

Jeśli tak nie jest, być może możesz napisać skrypt bash:

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

Naciśnij ctrl+ d:

chmod a+x myscript.sh
sudo myscript.sh

Mam nadzieję, że to pomoże.

użytkownik15453
źródło
1
sudo at now  
at> echo test > /tmp/test.out  
at> <EOT>  
job 1 at Thu Sep 21 10:49:00 2017  
użytkownik8648126
źródło
1
Jeśli sudotak, atto nie jest przydatne ani konieczne. sudo sh -c 'echo test >/tmp/test.out'robi to samo znacznie bardziej wydajnie i elegancko (ale wciąż ma wadę, że prawdopodobnie uruchamiasz rzeczy, rootktóre nie potrzebują tego uprawnienia; na ogół powinieneś unikać uprzywilejowanych poleceń, kiedy możesz).
tripleee