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