Такой ответ какэтотхорошо объясняет, как контролировать передачувсепеременных до команды.
Я хотел изучить, как это сделать на основе каждого аргумента. Обратите внимание (это было протестировано в 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
Вторая строка отличается от второй строки любой другой оболочки.
Это хорошая идеяпрочитайте ответы на этот вопрос