Перенаправление stdout в файл, на который у вас нет разрешения на запись

113

Когда вы пытаетесь изменить файл без разрешения на запись, вы получаете сообщение об ошибке:

> touch /tmp/foo && sudo chown root /tmp/foo
> echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Sudoing не помогает, потому что он запускает команду от имени пользователя root, но оболочка обрабатывает перенаправление stdout и в любом случае открывает файл как вы:

> sudo echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Существует ли простой способ перенаправить стандартный вывод в файл, на который у вас нет разрешения на запись, кроме открытия оболочки в качестве пользователя root и манипулирования этим способом?

> sudo su
# echo test > /tmp/foo
Майкл Mrozek
источник
2
Ответьте на аналогичный вопрос из StackOverflow stackoverflow.com/questions/82256/…
Кристиан Чупиту
Как вы можете не иметь разрешения на файл, который вы создали сами в TMP? это из-за масок?
k961
@ k961 Раньше я chownменял владельца; это был просто пример
Майкл Мрозек

Ответы:

116

Да, используя tee. Так echo test > /tmp/fooстановится

echo test | sudo tee /tmp/foo

Вы также можете добавить ( >>)

echo test | sudo tee -a /tmp/foo
Герт
источник
27
Ти также будет выводить на стандартный вывод; иногда вы не хотите, чтобы содержимое заполняло экран. Чтобы это исправить, сделайтеecho test | sudo tee /tmp/foo > /dev/null
Шон Дж. Гофф
Как вы будете делать это с heredoc?
JohnDoea
26

Заменить содержимое файла с выводом echo(как >оператор перенаправления оболочки).

echo test | sudo dd of=/tmp/foo

Чтобы записать в файл (в начале, хотя вы можете использовать seekдля вывода с разными смещениями) без усечения (например, 1<>оператор оболочки Bourne):

echo test | sudo dd of=/tmp/foo conv=notrunc

Чтобы добавить в файл (вроде >>), с помощью GNU dd:

echo test | sudo dd of=/tmp/foo oflag=append conv=notrunc

Смотрите также GNU dd, conv=exclчтобы избежать путаницы в существующем файле (например, set -o noclobberв оболочках POSIX), и conv=nocreatнаоборот (только обновить существующий файл).

Крис Джипуэй
источник
умная! это избавляет от необходимости делать, echo test | sudo tee /tmp/foo >/dev/nullчтобы отказаться от вывода.
Адам Кац
2
Возможно, мне придется забрать это обратно; ddэто ненадежно, если только вы не используете неясные опции только для GNU iflag=fullblock oflag=fullblock, которые убирают элегантность этого ответа. Я буду придерживаться tee.
Адам Кац
5
дд надежен с непонятным бс = 1
умебоши
2
@umeboshi Но надежный, только если вы достаточно опытны, чтобы точно знать, что вы делаете. Ибо ddможет быть довольно опасным (если не сказать: разрушительным), если была допущена лишь небольшая ошибка. Поэтому для новых пользователей я бы рекомендовал этот teeметод, чтобы быть на безопасном берегу.
syntaxerror
1
@AdamKatz, в случае dd of=fileодного (без count/ skip...), это надежно. iflag=fullblockне нужен, потому что здесь ddпишет на выходе, что он прочитал на входе. Неважно, если это не были полные блоки.
Стефан
12

tee Возможно, это лучший выбор, но в зависимости от вашей ситуации может быть достаточно чего-то подобного:

sudo sh -c 'echo test > /tmp/foo'
phunehehe
источник
1
3 задачи: evalвместо простой sh -c; -i, который изменит ваш рабочий каталог; запуск всей команды от имени root, что может изменить ее поведение или может привести к ненужному риску. почему это когда-либо получило голос?
4
Вы не сделали, но это вводит в заблуждение, как правило, плохо и, возможно, даже опасно.
5

Хотя я согласен, это | sudo teeканонический способ, иногда sed (здесь предполагается GNU sed) может работать:

cat sudotest 
line 1

sudo sed -i '1iitest' sudotest && cat sudotest 
itest
line 1

sudo sed -i '$aatest' sudotest && cat sudotest 
itest
line 1
atest

-iизменяет файл на месте. 1iозначает вставить перед строкой 1. $aозначает добавить после последней строки.

Или скопируйте в xclipboard:

somecommand | xclip
sudo gedit sudotest
move cursor to desired place, click middle mouse button to insert, save
неизвестный пользователь
источник
1
Это сформулировано как древняя китайская загадка. Не могу понять, как это вызвало столько голосов. ( hrmph )
синтаксическая ошибка
1
@syntaxerror: Сэр, извините, я не являюсь носителем английского языка. Если вы укажете, что вам неясно, я мог бы попытаться улучшить свой ответ. По сравнению с 65 голосами против Герта, 4 не так уж и много, не так ли?
пользователь неизвестен
Ну, я был бы очень доволен 4 :-D Ваш английский совсем не плохой. Это просто сформулировано в некотором роде технаря. Я должен догадаться, что вы кодер. Таким образом, кодеры друг друга прекрасно поймут себя, но не кодер должен думать, что это как ... как сказано.
синтаксическая ошибка
Обратите внимание, что на sed -iсамом деле файл не изменяется на месте - он создает временный файл и переименовывает его при выходе. Таким образом, вы не сможете сделать что-то похожее tail -f ...на исходный файл и увидеть результат, используя sed -i ...во время работы конвейера
Эндрю Хенле,
@AndrewHenle: Да, поскольку размер может быть увеличен или уменьшен, и поскольку это, вероятно, имеет место для большинства вызовов sed, и вы не можете даже - afaik - записывать в одно и то же место на SSD, это только псевдо-операция «на месте» , Как не говорящий по-английски, могу ли я попросить краткого выражения, которое, скорее всего, неверно истолковано? Просто -i creates a new file of same nameили есть что-то более компактное? Я думаю, мне нравится in place, потому что это объясняет i. Манн-страница gnu-sed также называет это на месте, и длинный флаг есть --in-place.
пользователь неизвестен
2

Я долго обдумывал идеи подобной проблемы и придумал следующие решения:

  • sudo uncatгде uncat- программа, которая читает стандартный ввод и записывает его в файл, указанный в командной строке, но я еще не написал uncat.

  • sudocatвариант, sudoeditкоторый я еще не написал, который делает уборщик sudo catили sudo uncat.

  • или этот маленький трюк использования sudoeditс редактором, который является сценарием оболочки

    #!/bin/sh
    # uncat
    cat > "$1"

    который может быть вызван как |sudo ./uncat fileили, | EDITOR=./uncat sudoeditно имеет интересные побочные эффекты.

hildred
источник
catпринимает список файлов для кон кошки inate, поэтому UNCAT должен взять список файлов для ООН мошенника кота inate к. Пришлось бы использовать магию, чтобы решить, сколько положить в каждый файл. Альтернативное название включает dog, to-file, redirect.
Ctrl-Alt-Delor
Я не могу думать ни о какой причине, почему я хотел бы, uncatкогда у меня есть tee.
Подстановочный
1
Хорошо, у teeнего есть тривиальный недостаток, заключающийся в том, что он записывает свой стандартный ввод в свой стандартный вывод - который тривиально смягчается путем перенаправления стандартного вывода в /dev/null. Другие альтернативы включают dd of=/tmp/foo(упомянутый в другом ответе), который записывает информацию о состоянии в stderr, и cp /dev/stdin /tmp/foo.
Скотт
1

Используйте губку из пакета moreutils. Преимущество в том, что он не пишет в стандартный вывод.

echo test | sudo sponge /tmp/foo

Используйте параметр -a, чтобы добавить файл вместо перезаписи.

Хонтвари Левенте
источник
1
Я не уверен, какое преимущество не дает запись в stdout, но вы могли бы просто перенаправить вывод, /dev/nullесли это было проблемой
Майкл Мрозек
1
Конечно, я могу перенаправить в / dev / null, но команду легче читать и печатать без перенаправления. Преимущество отсутствия записи на стандартный вывод состоит в том, что мой терминал не заполнен мусором.
Хонтвари Левенте
1
Я бы не стал устанавливать пакет, чтобы сэкономить перенаправление, но я регулярно пользуюсь губкой, поэтому она уже есть.
Хонтвари Левенте
0

Ошибка происходит от порядка, в котором оболочка делает вещи.

Перенаправление обрабатывается еще до запуска оболочки и sudo, следовательно, выполняется с разрешениями пользователя, с которым вы в данный момент работаете. Поскольку у вас нет разрешения на запись для создания / усечения цели перенаправления, вы получаете сообщение permission deniedоб ошибке из оболочки.

Решение состоит в том, чтобы гарантировать, что выходной файл будет создан под идентификатором, данным вам sudo, например, с помощью tee:

$ generate_output | sudo tee target_file
Кусалананда
источник