Как «расширить» переменную bash (Включенный код работает для bash, но не для zsh)

Как «расширить» переменную bash (Включенный код работает для bash, но не для zsh)

Такой ответ какэтотхорошо объясняет, как контролировать передачувсепеременных до команды.

Я хотел изучить, как это сделать на основе каждого аргумента. Обратите внимание (это было протестировано в zsh):

$ program() { echo 1: $1 2: $2 3: $3; }

$ run() { program "$@"; }

$ run2() { echo `run $1`; }

$ run2 'a b' c
1: a b 2: 3:

Мне нужен способ передать первый аргумент, который является строкой, содержащей пробелы, и объединить этот $@стиль в другую команду. Например, это run2 "a b" cдолжно выдать1: a 2: b 3:

На этом этапе я решил свою непосредственную проблему, потому что, хотя мой тестовый код и сломался при тестировании в zsh, после внедрения в реальный скрипт bash он заработал.

Это указывает на то, что, возможно, это зависит от некоторых тонкостей в обработке строк и аргументов, которые не являются "безопасными". Так что это скорее просьба прокомментировать более надежный способ надежного достижения этого поведения.

решение1

Разница между bash и zsh, которая здесь имеет значение, заключается в способе run2вызова run, а именно в эффекте отсутствия $1кавычек.

  • В zsh run $1применяет оператор «remove-if-empty» к $1, т. е. вызывается runс первым аргументом, который был передан в run2, за исключением того, что если первый аргумент run2был пустым (или если run2был вызван без аргумента), то runвызывается без аргумента.
  • В других оболочках в стиле Bourne, таких как bash, run $1к применяется оператор «split+glob» $1, т. е. он разбивает первый аргумент на run2фрагменты, разделенные пробелами¹, интерпретирует каждый фрагмент как шаблон подстановочных знаков² и заменяет каждый шаблон подстановочных знаков, соответствующий одному или нескольким файлам, списком совпадений.

Таким образом, run2 'a b' cвызовы runс аргументом a bв zsh (аргумент передается без изменений), но вызовы runс двумя аргументами aи bв bash (разбиваются на части, разделенные пробелами).

Мне нужен способ передать первый аргумент, который является строкой, содержащей пробелы, и объединить этот $@стиль в другую команду. Например, это run2 "a b" cдолжно выдать1: a 2: b 3:

Ваше описание и ваш пример говорят о разном. Если вы хотите передать первый аргумент другой команде, используйте , run "$1"чтобы убедиться, что аргумент не разделен. Передача аргументов без изменений — это весь смысл "$@".

Похоже, что на самом деле вы хотите разбить первый аргумент на run2куски, разделенные пробелами. В bash это можно сделать, отключив расширение подстановочных знаков, а затем (предполагая, что это IFSне изменено по сравнению с значением по умолчанию) используя расширение без кавычек.

run2 () ( set -f; run $1; )

( echo "$(somecommand)"по сути эквивалентно запуску somecommandв подоболочке, и, похоже, именно это вы имели в виду, а не то, echo $(somecommand)которое применяет split+glob к выходным данным команды, поэтому я удалил избыточную замену echo-command-substitution.)

В zsh вы можете использовать =символ в подстановке параметра, чтобы выполнить разделение мира (и без подстановки) для значения.

run2 () { run $=1; }

Синтаксис zsh несовместим с синтаксисом простого sh. Если вы хотите получить исходный скрипт sh из zsh, вы можете использовать сборку emulate:

emulate sh -c '. myscript.sh'

Используйте emulate kshдля эмуляции (некоторых функций) ksh. Это не эмулирует все функции bash, но позволяет использовать массивы.

¹ В более общем смысле, на основе значения IFS.
² Если это не было отключено с помощью set -f.

решение2

Добро пожаловать в мир цитирования и цитирующих сюрпризов.

Основная проблема в том, что zshпо умолчанию не разделяется на символы IFS.
В этом: он отличается от всех других оболочек.

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

Ваш код (добавив пару переменных):

program() { printf '%02d-1: %6s   2: %6s    3: %6s' "$i" "$1" "$2" "$3"; }
runa()  { program  "$@" ; }
run1()  { echo "`runa  $1 $2 $3 `"; }
run1    'a b' c

Позвольте мне подробнее остановиться на деталях.

Предположим, что вы создаете локальную shссылку, указывающую на местонахождение zsh:

ln -s "/usr/bin/zsh" ./sh

И еще, предположим, вы копируете следующий скрипт в soфайл.

Скрипт, повторяющий каждую функцию с переменными в кавычках и без кавычек:

program() { printf '%02d-1: %6s   2: %6s    3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@"; }
runb() { program  $@ ; }

run1() { echo "`runa  "$1" "$2" "$3"`"; }
run2() { echo "`runb  "$1" "$2" "$3"`"; }
run3() { echo "`runa  $1 $2 $3`"; }
run4() { echo "`runb  $1 $2 $3`"; }

for i in `seq 4`; do
    run"$i" 'a b' c
done

Затем, при выполнении, мы получаем следующее:

# Any shell (except zsh) results.
01-1:    a b   2:      c    3:
02-1:      a   2:      b    3:      c
03-1:      a   2:      b    3:      c
04-1:      a   2:      b    3:      c

Только первый запуск (run1), где все заключено в кавычки, остается 'a b'объединенным.

Однако zsh действует так, как будто все всегда было заключено в кавычки:

# ZSH results.
01-1:    a b   2:      c    3:
02-1:    a b   2:      c    3:
03-1:    a b   2:      c    3:
04-1:    a b   2:      c    3:

zsh в эмуляции.

Предполагается, что это zshбудет эмулировать старые оболочки, если вызывать их как shили ksh.
Но на практике это не всегда так:

$ ./sh ./so   # emulated sh
01-1:    a b   2:      c    3:
02-1:    a b   2:      c    3:
03-1:      a   2:      b    3:      c
04-1:      a   2:      b    3:      c

Вторая строка отличается от второй строки любой другой оболочки.

Это хорошая идеяпрочитайте ответы на этот вопрос

Связанный контент