Програма Bash не виконується, якщо перенаправлення не вдалося


9

В основному я зауважую, що якщо команда, що використовує переадресацію, не вдасться, будь-які програми, запущені до цього, не запускаються.

Наприклад, ця програма відкриває файл "a" і записує 50 байт у файл "a". Однак, запустивши цю команду з перенаправленням у файл з недостатніми дозволами (~ root / log), не змінюється розмір файлу "a".

$ ./write_file.py >> ~root/log
-bash: /var/root/log: Permission denied
cdal at Mac in ~/experimental/unix_write
$ ls -lt
total 16
-rw-rw-r--  1 cdal  staff  0 Apr 27 08:54 a <-- SHOULD BE 50 BYTES

Можна подумати, що програма запуститься, захопить будь-який вихід (але також запише у файл "a"), а потім не зможе записати жоден вихід у ~ root / log. Натомість програма ніколи не запускається.

Чому це так, і як bash вибирає порядок "перевірок", які він виконує перед виконанням програми? Чи проводяться також інші перевірки?

ps Я намагаюся визначити, чи дійсно програма, запущена під cron, виконувалась при переадресації на файл "дозволу відмовлено".


Все в належному робочому стані (тобто право власності та дозволи вашого файлу .py, який є) ваша програма виконує штрафи. Ваші проблеми пов'язані з переадресацією. Ви не маєте дозволу на написання файлу file / root. І ви перенаправили свою, stdoutщоб зробити саме це. Отже, ви не побачите жодного результату, навіть незважаючи на те, що ваша програма працювала.
Мельбурслан

2
Мел, це неправда, програма ніколи насправді не запускалася. Відповіді дивіться нижче.
Чарлі Далсасс

Ви: "Запустіть write_file.pyпрограму і надішліть її вихід ~root/logbash:" Вибачте, але вам не дозволяється писати в цей файл! "Оболонка робить саме те, що повинна робити. Якщо вона не може зробити те, про що ви її попросили зробіть це, він негайно повідомляє вас, чому виникає проблема, даючи вам можливість вирішити, як з цим вирішити проблему. Оскільки всі технічні працівники bash знають, дуже погані речі можуть трапитися, якщо ви запустите цю команду і не зможете зберегти вихід. Якщо це було досить важливо, ви визначили місце для його збереження, було б неправильно ASS | U | ME було нормально працювати без збереження stdout.
Monty Harder

Відповіді:


18

Це не насправді питання замовлення чеків, а просто порядок, в якому оболонка встановлює речі. Перенаправлення встановлюються перед виконанням команди; тож у вашому прикладі оболонка намагається відкрити ~root/logдля додавання, перш ніж намагатися зробити що-небудь, що стосується ./write_file.py. Оскільки файл журналу неможливо відкрити, перенаправлення не вдається, і оболонка перестає обробляти командний рядок у цьому пункті.

Один із способів продемонструвати це - взяти не виконуваний файл і спробувати запустити його:

$ touch demo
$ ./demo
zsh: permission denied: ./demo
$ ./demo > ~root/log
zsh: permission denied: /root/log

Це показує, що оболонка навіть не дивиться, ./demoколи перенаправлення неможливо встановити.


Ух, це так просто? Я не усвідомлював, що переадресації робляться спочатку. Дякую за цю відповідь, а також за інші чудові відповіді.
Чарлі Далсасс

6
Якби вони не були зроблені першими, куди було б записати вихід?
Чарльз Даффі

І якщо вихід не може бути записаний, як ми можемо знати, що безпечно запустити команду? Можливо, команда виводить інформацію, яка видаляється з сховища даних, і абсолютно необхідно, щоб результат був захоплений. Добре, що Баш не дозволить йому працювати, поки ви не виправите ці дозволи, так?
Monty Harder

11

З сторінки " bash man", розділ "ПОПЕРЕДЖЕННЯ" (акцент на мене):

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

...

Невміння відкрити або створити файл спричиняє збій перенаправлення.

Отже оболонка намагається відкрити цільовий файл для stdout, який не вдається, і команда взагалі не виконується.


Дуже дякую. Я б хотів, щоб сторінка man трохи уточнила з "... якщо вихід не вдасться перенаправити, програма не буде виконуватися".
Чарлі Далсасс

Оновлено; досить приховано деякі параграфи нижче.
Мерфі

Насправді це досить зрозуміло. "Невміння відкрити або створити файл призводить до невдалого перенаправлення." Там. Знову дякую.
Чарлі Далсасс

3

Варто зауважити, що оболонка повинна встановити перенаправлення перед запуском програми.

Розглянемо ваш приклад:

./write_file.py >> ~root/log

Що відбувається в оболонці:

  1. Ми (оболонка) fork(); дочірній процес успадковує дескриптори відкритого файлу від свого батьківського (оболонки).
  2. У дочірньому процесі ми fopen()(розширення) "~ root / log", і dup2()це до fd 1 (і close()тимчасовий fd). Якщо fopen()не exit()вдалося , зателефонуйте, щоб повідомити про помилку батьків.
  3. Ще в дитині ми exec()"./write_file.py". Зараз цей процес більше не виконує жодного нашого коду (якщо не вдалося виконати, і в цьому випадку ми exit()повідомимо про помилку батьків).
  4. Батько wait()зобов'язаний дитину припинити та обробляти її вихідний код (копіюючи його $?, принаймні).

Отже, перенаправлення повинно відбуватися у дитини між fork()і exec(): воно не може відбутися раніше, fork()оскільки воно не повинно змінювати викладку оболонки, а не може відбутися після, exec()тому що ім'я файлу та виконуваний код оболонки тепер замінено програмою Python . Батько не має доступу до дескрипторів файлів дитини (і навіть якщо це було, він не міг гарантувати переадресацію між exec()першим і записом до stdout).


0

Мені шкода, що повідомляю, що це зовсім навпаки. Оболонці потрібно спочатку відкрити це введення / вивід, а потім передає управління програмі.

tee може виявитися корисним у цьому випадку: ./write_file.py | tee -a ~root/log > /dev/null


Чи не просто сценарій Python загине на SIGPIPE після відмови трійника?
Кевін

Не відповідно до тесту, який я зробив, але я пропоную вам спробувати.
Джулі Пелтьє
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.