Почему/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.