Почему/bin/sh ведет себя по-разному в /bin/ bash, даже если один указывает на другой?
Пока я играл в моей оболочке, расследуя ответ на этот вопрос, я заметил, что, хотя /bin/sh
указывал на /bin/bash
в моей системе, две команды ведут себя по-разному. Прежде всего, вывод
ls -lh /bin/sh
является:
lrwxrwxrwx 1 root root 4 Apr 22 2013 /bin/sh -> bash*
Однако, вызывая следующую команду через /bin/sh
:
/bin/sh -c "script.sh 2> >( grep -v FILTER 2>&1 )"
возвращает эту ошибку:
/bin/sh: -c: line 0: syntax error near unexpected token '>'
/bin/sh: -c: line 0: 'script.sh 2> >( grep -v FILTER 2>&1 )'
Выполняя ту же команду через /bin/bash
:
/bin/bash -c "script.sh 2> >( grep -v FILTER 2>&1 )"
выполняется успешно, вот результат:
This should be on stderr
Для справки, вот содержание script.sh
:
#!/bin/sh
echo "FILTER: This should be filtered out" 1>&2
echo "This should be on stderr" 1>&2
echo "FILTER: This should be filtered out" 1>&2
Почему две вызовы ведут себя по-другому?
Ответы
Ответ 1
bash
смотрит значение $argv[0]
(bash реализовано в C), чтобы определить, как он был вызван.
Его поведение при вызове sh
документируется в руководстве:
Если Bash вызывается с именем sh
, он пытается имитировать запуск поведение исторических версий sh
как можно ближе, в то время как в соответствии с стандартом POSIX.
При вызове в качестве интерактивной оболочки входа или в качестве неинтерактивного shell с опцией -login
, он сначала пытается прочитать и выполнить команд из /etc/profile
и ~/.profile
в этом порядке. Опция --noprofile
может использоваться для подавления этого поведения. При вызове как интерактивной оболочки с именем sh
, Bash ищет переменную ENV
, расширяет свое значение, если оно определено, и использует расширенное значение как имя файла для чтения и выполнения. Поскольку оболочка, вызываемая как sh
не пытается читать и выполнять команды из любого другого запуска файлов, параметр --rcfile
не действует. Неинтерактивная оболочка вызывается с именем sh
, не пытается прочитать какой-либо другой запуск файлы.
При вызове sh
, Bash переходит в режим POSIX после того, как файлы запуска прочитать
Там длинный список (в настоящее время 46 элементов) вещей, которые меняются, когда bash
находится в режиме POSIX, описанный здесь.
(Режим POSIX, вероятно, полезен главным образом как способ тестирования скриптов для переносимости на оболочки не bash
.)
Кстати, программы, которые меняют свое поведение в зависимости от имени, под которым они были вызваны, довольно распространены. Некоторые версии grep
, fgrep
и egrep
реализованы как один исполняемый файл (хотя GNU grep
этого не делает). view
обычно является символической ссылкой на vi
или vim
; вызывая его как view
, вызывает открытие в режиме только для чтения. Система Busybox включает в себя несколько отдельных команд, которые являются символическими ссылками на исполняемый файл master busybox
.
Ответ 2
Вызов bash как sh
заставляет его входить в режим posix после чтения файлов запуска, которые он обычно читал (в отличие от файлов запуска, которые POSIX sh читал.) bash имеет много разных режимов вызова. Вы можете узнать об этих режимах из раздела INVOCATION
руководства. Вот несколько подробностей о режиме POSIX.
Режим POSIX
Этот режим означает, что bash будет пытаться в разной степени соответствовать ожиданиям POSIX. Как объяснено здесь, bash имеет несколько разных вызовов для этого режима, с немного разными последствиями:
-
sh
: bash переходит в режим POSIX после чтения файлов запуска.
-
bash --posix
: bash переходит в режим POSIX перед чтением файлов запуска.
-
set -o posix
: bash переключается в режим POSIX.
-
POSIXLY_CORRECT
: Если эта переменная находится в среде при запуске bash, оболочка переходит в режим posix перед чтением загрузочных файлов, например bash --posix
. Если он установлен во время выполнения bash, например set -o posix
.
Ответ 3
Из справочного руководства Bash:
Если Bash вызывается с именем sh, он пытается как можно ближе имитировать поведение при запуске исторических версий sh, в то же время соответствующее стандарту POSIX.
Ответ 4
Поскольку двоичный файл bash
проверяет, как он был вызван (через argv[0]
), и переходит в режим совместимости, если он выполняется как sh
.