Это довольно сложно.
Сначала обратите внимание, что внутри не работает сглаживание [[ ]]
. Если вы запускаете что-то вроде
set -x; [[ / == /* ]]; echo $?
тогда вы увидите, /*
что не расширяется, как в echo /*
.
Теперь есть extglob
вариант оболочки. Похоже, в вашем случае он включен в интерактивных оболочках, отключен в неинтерактивных. (Сравните. Где в моей интерактивной оболочке включен «shopt extglob»? )
Если
extglob
опция оболочки включена с помощьюshopt
встроенной функции, распознаются несколько расширенных операторов сопоставления с образцом. [...]
!(pattern-list)
Совпадает с чем угодно, кроме одного из заданных шаблонов.
( источник )
Внутри [[ ]]
оператор распознается, но не расширяется. Откуда мне знать? Я могу заставить оболочку не узнавать это:
shopt -u extglob set -x; [[ !(-z "") ]]; echo $?
Теперь я получаю -bash: !: event not found
. Это указывает на !
другое значение:
!
Запустите подстановку истории, за исключением случаев, когда за ней следует пробел, табуляция, конец строки=
или(
(когдаextglob
опция оболочки включена с помощьюshopt
встроенной функции).
( источник )
Это отличается между интерактивными и неинтерактивными оболочками:
Когда оболочка работает в интерактивном режиме, она меняет свое поведение несколькими способами.
[…]
7. История команд […] и расширение истории […] включены по умолчанию.
( источник )
Следующий шаг - отключить историю:
shopt -u extglob set +o history set -x; [[ !(-z "") ]]; echo $?
И вот, результат, как в вашей неинтерактивной оболочке.
Можем ли мы сделать это наоборот? Можем ли мы заставить неинтерактивную оболочку вести себя как интерактивная?
Сначала давайте проверим, что происходит, когда новая оболочка вынуждена быть интерактивной:
bash -ic 'set -x; [[ !(-z "") ]]; echo $?'
Он ведет себя, как и ожидалось, как ваша интерактивная оболочка. Теперь давайте включим extglob
неинтерактивную оболочку:
bash -c 'shopt -s extglob; set -x; [[ !(-z "") ]]; echo $?'
И это не работает! Он по-прежнему ведет себя как ваша неинтерактивная оболочка с extglob
отключенным. Объяснение простое, но не очевидное: глобализация выполняется для всей строки, прежде чем shopt
она выполнит свою работу. Когда опция меняется, уже слишком поздно. Давайте разделим команду на две строки:
bash -c 'shopt -s extglob set -x; [[ !(-z "") ]]; echo $?'
Теперь он ведет себя как ваша интерактивная оболочка.
Последняя загадка заключается в следующем:
+ [[ -n !(-z ) ]]
Откуда -n
взялась ваша интерактивная оболочка? Моя гипотеза основана на наблюдении, [[ "random-string" ]]
которое рассматривается как [[ -n "random-string" ]]
(проверьте его с помощью set -x
). Я предполагаю, что когда вы запускаете [[ !(-z "") ]]
и extglob
включаете, тот факт, что !( )
оператор распознается, но не раскрывается, заставляет всю !(-z "")
строку оставаться там как одно слово, т.е.
set -x; [[ "!(-z )" ]]; echo $?
Эта команда (в интерактивной или неинтерактивной оболочке) ведет себя так же, как та, которую вы запускаете в интерактивной оболочке.