Приоритет && vs & in bash и zsh

Отвечая на этот вопрос, я обнаружил очень забавную (и тонкую) разницу между поведением в bash и zsh :

В bash :

 romano@RRyS:~$ pwd /home/romano romano@RRyS:~$ alias x="cd /bin && ./echo A >/dev/null &" romano@RRyS:~$ x [1] 16611 romano@RRyS:~$ pwd /home/romano 

Как вы можете видеть, выполнение псевдонима x выполняется в подоболочке и поэтому текущий каталог не изменяется.

Не в zsh :

 [romano:~] % pwd /home/romano [romano:~] % alias x="cd /bin && ./echo A >/dev/null &" [romano:~] % x [1] 16744 [1] + 16744 done ./echo A >/dev/null 1& [romano:/bin] % pwd /bin [romano:/bin] % 

здесь каталог изменяется.

Кажется, что & in bash имеет другой приоритет, чем в zsh — я имею в виду, что команда, похоже, читается как

 (cd /tmp && echo A) & 

в bash и as

 cd /tmp && (echo A &) 

в zsh . Это правильно или причина другого поведения – это другое?

Различное документированное поведение в zshmisc

Список представляет собой последовательность из нуля или более подписок, в которой каждый подсчет заканчивается ; , & , &| , &! , или новой строки. Этот ограничитель необязательно может быть опущен из последнего списка в списке, когда список отображается как сложная команда внутри (...) или {...} . Когда подписчик заканчивается ; или новой строки, оболочка ждет его завершения до выполнения следующего подсписок. Если суб-список завершается символом & , &| , или &! , оболочка выполняет последний конвейер в нем в фоновом режиме и не ждет его завершения (обратите внимание на отличие от других оболочек, которые выполняют весь подсписчик в фоновом режиме). Фоновый конвейер возвращает состояние нуля.

Погребенный в zshmisc(1) – следующая строка:

Если подсписчик завершается символом &', & \' или `&! ', Оболочка выполняет последний конвейер в нем в фоновом режиме,

Хотя в нем конкретно не указано, что другие конвейеры в подсписке выполняются в текущей оболочке, это похоже на то, что оно подразумевает, и поведение, которое вы наблюдаете, поддерживает эту интерпретацию. Например:

 $ echo $foo $bar $ foo=3 && bar=5 && sleep 1 & $ echo $foo $bar 3 5 

также поддерживает представление о том, что первые два конвейера выполняются в текущей оболочке, и только последний конвейер подписок фактически выполняется в фоновом режиме.