Перенаправлення 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

Чи є простий спосіб перенаправити stdout у файл, у який ви не маєте права писати, окрім відкриття оболонки як root та маніпулювання файлом таким чином?

> sudo su
# echo test > /tmp/foo

2
Відповідь на таке запитання від StackOverflow stackoverflow.com/questions/82256 / ...
Cristian Ciupitu

як у вас немає дозволу на файл, який ви створили самостійно в tmp? це від умаска?
k961

@ k961 я раніше chownміняв власника; це був лише приклад
Майкл Мрозек

Відповіді:


116

Так, використовуючи tee. Так echo test > /tmp/fooстає

echo test | sudo tee /tmp/foo

Ви також можете додати ( >>)

echo test | sudo tee -a /tmp/foo

27
Трійник також буде виводити в stdout; іноді не хочеться, щоб вміст заповнював екран. Щоб виправити це, зробітьecho test | sudo tee /tmp/foo > /dev/null
Шон Дж. Гофф

Як ви це зробите з гередоком?
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's, conv=exclщоб уникнути крадіжки існуючого файлу (як, наприклад, set -o noclobberв оболонках POSIX) і conv=nocreatнавпаки (лише оновити існуючий файл).


розумний! це полегшує необхідність зробити, echo test | sudo tee /tmp/foo >/dev/nullщоб відкинути вихід.
Адам Кац

2
Можливо, мені доведеться взяти це назад; ddдля цього ненадійний, якщо ви не використовуєте неясні параметри iflag=fullblock oflag=fullblock, що стосуються лише GNU , які усувають елегантність цієї відповіді. Я буду дотримуватися tee.
Адам Кац

5
dd надійний із неясними bs = 1
umeboshi

2
@umeboshi Але надійно, лише якщо ти достатньо досвідчений, щоб точно знати, що ти робиш. Бо ddможе бути досить небезпечним (якщо не сказати: руйнівним), якщо була допущена лише незначна помилка. Тому для нових користувачів я б краще рекомендував teeметод знаходитись на безпечному березі.
синтаксичний помилок

1
@AdamKatz, у випадку dd of=fileодного (без count/ skip...), це надійно. iflag=fullblockне потрібен, оскільки тут ddпише на виході те, що він прочитав на вході. Не має значення, чи це були не повні блоки.
Стефан Шазелас

12

tee це, мабуть, найкращий вибір, але залежно від вашої ситуації щось подібне може бути достатньо:

sudo sh -c 'echo test > /tmp/foo'

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 manpage також називає його на місці, і довгий прапор є --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але який має цікаві побічні ефекти.


catприймає список файлів для кін кішки inate, тому UNCAT повинен взяти список файлів для ООН шахрая кота inate к. Потрібно було б використовувати магію, щоб вирішити, скільки потрібно помістити у кожен файл. Альтернативне назва включає dog, to-file, redirect.
ctrl-alt-delor

Я не можу придумати жодної причини, чому я хотів би, uncatколи маю tee.
Wildcard

1
Ну, teeє тривіальний недолік, що він пише stdin до свого stdout - який тривіально пом’якшується шляхом перенаправлення stdout на /dev/null. Інші альтернативи включають dd of=/tmp/foo(згадується в іншій відповіді), яка записує інформацію про стан у stderr, та cp /dev/stdin /tmp/foo.
Скотт

1

Використовуйте губку з пакету moreutils. Він має ту перевагу, що він не пише в stdout.

echo test | sudo sponge /tmp/foo

Використовуйте опцію -a, щоб додати файл, а не перезаписувати його.


1
Я не впевнений, яка перевага не писати в подарунки stdout, але ви можете просто перенаправити вихід, /dev/nullякщо це буде проблема
Michael Mrozek

1
Звичайно, я можу переадресувати на / dev / null, але команду легше читати та набирати без перенаправлення. Перевага не писати в stdout полягає в тому, що мій термінал не заповнений сміттям.
Хонтварі Левенте

1
Я б не встановлював пакет, щоб заощадити перенаправлення, але регулярно використовую губку, тому вона вже є.
Хонтварі Левенте

0

Помилка виходить із порядку, в якому оболонка робить речі.

Перенаправлення обробляється до того, як оболонка навіть виконається sudo, і тому робиться з дозволами користувача, з яким ви зараз працюєте. Оскільки у вас немає дозволу на запис для створення / скорочення цілі переадресації, ви отримуєте permission deniedпомилку з оболонки.

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

$ generate_output | sudo tee target_file
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.