Программа Bash не выполняется, если перенаправление не удастся

9

В bash я заметил, что если команда, использующая перенаправление, потерпит неудачу, любые программы, которые выполняются до этого, не запускаются.

Например, эта программа открывает файл «а» и записывает 50 байтов в файл «а». Однако выполнение этой команды с перенаправлением в файл с недостаточными разрешениями (~ 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), ваша программа работает нормально. Ваши проблемы происходят от перенаправления. У вас нет разрешения на запись файла в / корневой каталог. И вы перенаправили свой, stdoutчтобы сделать именно это. Таким образом, вы не увидите никакого вывода, даже если ваша программа работала.
MelBurslan
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
Если бы они не были сделаны первыми, куда бы был записан вывод?
Чарльз Даффи
И если вывод не может быть записан, как мы можем знать, безопасно ли выполнять команду? Возможно, команда выводит информацию, которая удаляется из хранилища данных, и абсолютно необходимо, чтобы выходные данные были записаны. Хорошо, что bash не даст запустить, пока вы не исправите эти разрешения, а?
Монти Хардер
11

Со страницы руководства bash, раздел REDIRECTION (выделено мной):

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

...

Ошибка открытия или создания файла приводит к сбою перенаправления.

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

Мерфи
источник
Спасибо. Я бы хотел, чтобы страница справки немного прояснилась словами «... если вывод не может быть перенаправлен, программа не будет выполнена».
Чарли Далсасс
Обновлено; это довольно скрытый некоторые параграфы ниже.
Мерфи
На самом деле, это довольно ясно. «Ошибка открытия или создания файла приводит к сбою перенаправления». Вот оно Еще раз спасибо.
Чарли Далсасс
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()и первой записью в стандартный вывод).

Тоби Спейт
источник
0

Мне жаль сообщать вам, что это совсем наоборот. Оболочка должна сначала открыть свой ввод-вывод, а затем передать управление программе.

tee может оказаться полезным в этом случае: ./write_file.py | tee -a ~root/log > /dev/null

Джули Пеллетье
источник
Разве скрипт Python просто не умрет на SIGPIPE после того, как произойдет сбой?
Кевин
Не в соответствии с тестом, который я сделал, но я предлагаю вам попробовать.
Джули Пеллетье