Как использовать sudo для перенаправления вывода в местоположение, в которое у меня нет разрешения на запись?

881

Мне был предоставлен доступ sudo к одному из наших Linux-пакетов RedHat для разработки, и я, похоже, довольно часто нуждаюсь в перенаправлении вывода в местоположение, к которому у меня обычно нет доступа для записи.

Проблема в том, что этот надуманный пример не работает:

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

Я просто получаю ответ:

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

Как я могу заставить это работать?

Джонатан
источник
1
используйте chmod u + w имя файла
Сабольч Домби
@DombiSzabolcs Вы предлагаете сначала создать файл как sudo, а затем дать себе разрешение? Хорошая идея.
Джонатан
Во многих ситуациях вы попадаете сюда, потому что спрашиваете «почему мне отказывают в разрешении?» Иногда ответ заключается в том, что вам нужно создать файл как root(в этом случае перейдите к чтению ответов), но очень часто вам просто нужно создать файл где-то еще, как вы сами.
Трипли
1
После борьбы с этими ответами я, наконец, решил перенаправить во временный файл и sudo переместить его в место назначения.
TingQian LI
См. Также stackoverflow.com/questions/84882/…
Нейт Элдридж

Ответы:

1233

Ваша команда не работает, потому что перенаправление выполняется вашей оболочкой, у которой нет разрешения на запись /root/test.out. Перенаправление вывода не выполняется sudo.

Есть несколько решений:

  • Запустите оболочку с помощью sudo и введите команду, используя -cпараметр:

    sudo sh -c 'ls -hal /root/ > /root/test.out'
    
  • Создайте скрипт с вашими командами и запустите этот скрипт с помощью sudo:

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

    Беги sudo ls.sh. Посмотрите ответ Стива Беннетта, если вы не хотите создавать временный файл.

  • Запустите оболочку, а sudo -sзатем запустите ваши команды:

    [nobody@so]$ sudo -s
    [root@so]# ls -hal /root/ > /root/test.out
    [root@so]# ^D
    [nobody@so]$
    
  • Используйте sudo tee(если вам приходится много избегать при использовании -cопции):

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

    Редирект /dev/nullнеобходимо для остановки тройник с выводом на экран. Чтобы добавить вместо перезаписи выходной файл ( >>), используйте tee -aили tee --append(последний относится к GNU coreutils ).

Благодарю Jd , Adam J. Forster и Johnathan за второе, третье и четвертое решения.

Кристиан Чиупиту
источник
1
Здесь есть отличный ответ, который расскажет вам, как перенаправить STDERR и STDOUT отдельно: stackoverflow.com/questions/692000/… ... в основномperl -e 'print "STDIN\n"; print STDERR "STDERR\n"; ' > >( tee stdout.log ) 2> >( tee stderr.log >&2 )
errant.info
2
Вы захотите сделать 'sudo -E ...', чтобы использовать переменные в команде shelled out (например, при использовании этого в скрипте).
Urhixidur
3
Перенаправление вывода тройника в / dev / null, вероятно, не требуется во многих случаях, когда вывод вывода на экран безвреден. Например, при работе только с выводом обычных команд или содержимым небольших текстовых файлов.
Томасруттер
105

Кто-то здесь только что предложил sudoing tee:

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

Это также может быть использовано для перенаправления любой команды в каталог, к которому у вас нет доступа. Это работает, потому что программа tee фактически является программой «echo to a file», и перенаправление на / dev / null - это остановка ее вывода на экран, чтобы сохранить ее такой же, как в оригинальном надуманном примере выше.

Джонатан
источник
5
Во многих случаях, а именно, если у обычного пользователя есть права доступа для выполнения команды и «только» не может записать в желаемый выходной файл, первый sudo(т. Е. Для самой команды) может быть опущен
Hagen von Eitzen
81

Уловка, которую я понял сам, была

sudo ls -hal /root/ | sudo dd of=/root/test.out
rhlee
источник
16
sudo ddлучше, чем sudo tee /root/file > /dev/nullпримеры выше!
kristianlm
14
дд не странная непонятная команда. Он используется всякий раз, когда вам нужно скопировать большие объемы данных с буферизацией между двумя блочными устройствами. Синтаксис на самом деле довольно прост, ddэто имя команды, of=/root/test.outэто аргумент, который говорит, ddчто такое выходные файлы.
Rhlee
14
@steve Все «неясно», пока вы не узнаете, что это такое. ofозначает выходной файл и ddявляется очень популярным инструментом, используемым как в Linux, так и в OSX (в основном для записи образов на диски). Это хитрый трюк наверняка.
blockloop
12
ddвероятно, в равной степени полезно, как teeздесь. В обоих случаях вы используете обычную, хорошо известную команду для цели, которая, хотя и немного отличается от ее первоначального предназначения, по-прежнему хорошо известна и хорошо документирована. Хотя ddон хорош в копировании огромных объемов данных, он не сосет с небольшими объемами. Это имеет то преимущество, что не выводит вывод на стандартный вывод.
Томасруттер
26
Всякий раз, когда вы вводите слова sudo ddрядом друг с другом, вы очень и очень уверены, что приведенные ниже аргументы верны (особенно учитывая его нестандартный синтаксис). Они не называют это «разрушитель диска» даром ...
ali_m
45

Проблема в том, что команда запускается sudo, но перенаправление запускается под вашим пользователем. Это делается оболочкой, и вы мало что можете с этим поделать.

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

Обычные способы обойти это:

  • Оберните команды в скрипт, который вы вызываете под sudo.

    Если команды и / или файл журнала изменяются, вы можете заставить скрипт принимать их в качестве аргументов. Например:

    sudo log_script command /log/file.txt
    
  • Вызовите оболочку и передайте командную строку в качестве параметра с -c

    Это особенно полезно для составных команд. Например:

    sudo bash -c "{ command1 arg; command2 arg; } > /log/file.txt"
    
ДСМ
источник
23

Еще одна вариация на тему:

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

Или конечно:

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

У них есть (крошечное) преимущество в том, что вам не нужно запоминать аргументы sudoили sh/bash

Стив Беннетт
источник
18

Разъяснение, почему предпочтительным является вариант тройника

Предполагая, что у вас есть соответствующее разрешение на выполнение команды, которая создает выходные данные, если вы перенаправили выходные данные своей команды на tee, вам нужно только повысить привилегии tee с помощью sudo и направить tee для записи (или добавления) в рассматриваемый файл.

в примере, приведенном в вопросе, это будет означать:

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

для пары более практических примеров:

# 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

В каждом из этих примеров вы берете выходные данные непривилегированной команды и записываете в файл, который обычно доступен для записи только пользователю root, что является источником вашего вопроса.

Это хорошая идея, потому что команда, которая генерирует вывод, не выполняется с повышенными привилегиями. Здесь это не имеет значения, echoно когда исходная команда представляет собой сценарий, которому вы не полностью доверяете, это очень важно.

Обратите внимание, что вы можете использовать опцию -a, чтобы добавить add (например >>) в целевой файл, а не перезаписать его (например >).

jg3
источник
Извините, js3, но это уже было предложено (еще в 2008 году), и он занимает второе место по ответу: stackoverflow.com/a/82553/6910
Джонатан
2
Вы правы, Джонатан, я обновлю свой ответ, чтобы раскрыть причины, почему это предпочтительный вариант. Спасибо за полезный отзыв.
jg3
17

Заставьте sudo запустить оболочку, например так:

sudo sh -c "echo foo > ~root/out"
Пенфолда
источник
11

Я бы хотел решить эту проблему:

Если вам нужно написать / заменить файл:

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

Если вам нужно добавить в файл:

echo "some text" | sudo tee -a /path/to/file
Никола Петкански
источник
1
Как это существенно отличается от ответов выше?
Джонатан
2
Это не «существенно отличается», но разъясняет отличительное использование между заменой и добавлением в файл.
Джамадагни
1
Это ответ, который я скопировал на мою шпаргалку.
MortimerCat
5

Как насчет написания сценария?

Имя файла: myscript

#!/bin/sh

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

# end script

Затем используйте sudo для запуска скрипта:

sudo ./myscript

источник
4

Всякий раз, когда мне нужно сделать что-то подобное, я просто становлюсь пользователем root:

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

Это, вероятно, не лучший способ, но это работает.

Адам Дж. Форстер
источник
4

Я бы сделал это так:

sudo su -c 'ls -hal /root/ > /root/test.out'
fardjad
источник
2
Это на самом деле кажется немного чище, так как вам не нужно явно указывать оболочку.
Стив Беннетт
1
Один небольшой недостаток состоит в том, что он запускает еще один процесс ( 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

Не хочу бить мертвую лошадь, но есть слишком много ответов здесь , что использование tee, что означает , что вы должны перенаправить stdoutк , /dev/nullесли вы не хотите , чтобы увидеть копию на экране.

Более простое решение - просто использовать catтак:

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

Обратите внимание, как перенаправление помещается в кавычки, чтобы оно оценивалось оболочкой, запущенной sudoвместо той, которая его запускает.

haridsv
источник
Это работает просто отлично. Я не понимаю, почему у него был один отрицательный голос. Upvoted.
Теему Лейсти
4
Я думаю, что это не получает много любви, потому что это не намного лучше, чем sudo bash -c "ls -hal /root > /root/test.out". Использование тройника позволяет избежать необходимости в раковине, а кошка - нет.
Ник Руссо
2

Это основано на ответе с участием tee. Чтобы упростить ситуацию, я написал небольшой скрипт (я его называю suwrite) и вставил его /usr/local/bin/с +xразрешения:

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

Как показано в ИСПОЛЬЗОВАНИИ в коде, все, что вам нужно сделать, - это передать выходные данные в этот скрипт, за которым следует желаемое доступное для суперпользователя имя файла, и оно при необходимости автоматически запросит ваш пароль (поскольку он включает в себя sudo).

echo test | suwrite /root/test.txt

Обратите внимание, что, поскольку это простая оболочка tee, она также примет -aопцию tee для добавления, а также поддерживает запись в несколько файлов одновременно.

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

Он также имеет некоторую упрощенную защиту от записи на /dev/устройства, которая была упомянута в одном из комментариев на этой странице.

Джамадагни
источник
1

Может быть, вы получили доступ sudo только к некоторым программам / путям? Тогда нет способа делать то, что вы хотите. (если вы не взломаете это как-то)

Если это не так, то, возможно, вы можете написать скрипт bash:

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

Нажмите ctrl+ d:

chmod a+x myscript.sh
sudo myscript.sh

Надеюсь, это поможет.

user15453
источник
1
sudo at now  
at> echo test > /tmp/test.out  
at> <EOT>  
job 1 at Thu Sep 21 10:49:00 2017  
user8648126
источник
1
Если у вас есть sudoвсе равно, atне полезно или не нужно. sudo sh -c 'echo test >/tmp/test.out'делает то же самое намного эффективнее и элегантнее (но все же страдает недостатком, заключающимся в том, что вы, вероятно, запускаете вещи, для rootкоторых эта привилегия не нужна; обычно вы должны избегать привилегированных команд, когда можете).
tripleee