Bash -c с позиционными параметрами

15

Обычно $0в сценарии задается имя сценария или как он был вызван (включая путь). Однако, если я использую bashэту -cопцию, $0устанавливается первый из аргументов, переданных после командной строки:

bash -c 'echo $0 ' foo bar 
# foo 

По сути, кажется, что позиционные параметры были смещены, но в том числе $0. Однако shiftв командной строке не влияет $0(как обычно):

bash -c 'echo $0; shift; echo $0 ' foo bar
# foo
# foo

Почему это явно странное поведение для командных строк? Обратите внимание, что я ищу причину, обоснование для реализации такого странного поведения.


Можно предположить, что такой командной строке не понадобится $0параметр, как обычно определяется, поэтому для экономии он также используется для обычных аргументов. Однако в этом случае поведение shiftстранно. Другая возможность заключается в том, что $0она используется для определения поведения программ (а-ля, bashназываемых shили vimназываемых как vi), но этого не может быть, поскольку $0здесь это видно только в командной строке, а не в программах, вызываемых внутри нее. Я не могу думать ни о каком другом использовании для $0, поэтому я затрудняюсь объяснить это.

Мур
источник
Как примечание стороны, я видел использование -для $0аргумента как идиомы, как и в sh -c 'foo $1 $2' - a b. Таким образом, это выглядит довольно нормально (как только вы узнали, что это -значит, то есть)
Volker Siegel
Вы также можете echo 'echo the other side of this pipe globs "$@"' | sh -s -- *, хотя, к сожалению, $0обычно это не настраиваемый параметр с параметром -stream ... Хотя, в общем, его можно использовать многими из тех же способов xargs. И другие, кроме того.
mikeserv
@VolkerSiegel Более нормальным было бы --то, что могло бы иметь обычную интерпретацию «отсюда начинаются аргументы», что можно увидеть в некоторых других программах. Опять же, это может сбить с толку тех, с кем мы не знаем, -cчто на --самом деле такое толкование есть.
Муру

Ответы:

10

Это дает вам возможность устанавливать / выбирать $0при использовании встроенного скрипта. Иначе $0бы просто было bash.

Тогда вы можете сделать, например:

$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter null or not set

Не все снаряды делали это. Оболочка Борна сделала. Оболочка Korn (и Almquist) предпочла, чтобы $1вместо нее использовался первый параметр . POSIX в конце концов пошел на пути Борна, так kshи ashпроизводные вернулись к тому , что позже (подробнее об этом в http://www.in-ulm.de/~mascheck/various/find/#shell ). Это означало, что долгое время sh(который, в зависимости от системы, основывался на оболочке Bourne, Almquist или Korn), вы не знали, был ли введен первый аргумент, $0или $1, поэтому для переносимости, вам приходилось делать что-то вроде:

sh -c 'echo foo in "$1"' foo foo

Или:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

К счастью, POSIX указал новое поведение, в котором $0вводится первый аргумент , так что теперь мы можем переносить:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt
Стефан Шазелас
источник
Я просто побежал: bash -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txtна Ubuntu 14.04, Баш version 4.3.11(1)-release, и я получил: txt files are *.txt. Оо
Муру
2
@muru, что правильно (и у вас, вероятно, нет txtфайлов в текущем каталоге). Смотрите такжеbash -c 'echo "${1?}"' foo
Стефан Шазелас
О да. Это практически полезный пример.
Муру
1
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txtэто фантастически изобретательно!
iruvar
Или вы используете полностью надежный обходной путь (предложенный Стефаном Шазеласом в comp.unix.shell) SHELL -c 'shift $1; command' 2 1 arg1 arg2 ... хаха ...
mikeserv
3

Это поведение определяется POSIX :

sh -c имя_команды [аргумент ...]

Чтение команд из операнда command_string. Установите значение специального параметра 0 (см. Специальные параметры ) из значения операнда command_name и позиционных параметров ($ 1, $ 2 и т. Д.) В последовательности из оставшихся операндов аргумента.

Что касается того, почему вы хотите такое поведение: это сглаживает разрыв между скриптом и -cстрокой. Вы можете напрямую конвертировать между ними без каких-либо изменений в поведении. Другие области полагаются, что они идентичны.

Это также согласуется с тем, как работают программные аргументы в целом: это в конечном итоге сводится к вызову одной из execфункций, в которой также указан первый предоставленный аргумент $0, и в равной степени обычно этот аргумент совпадает с исполняемым файлом, который вы запускаете. Иногда, тем не менее, вам нужна особая ценность, и другого способа получить ее просто не было. Учитывая, что аргумент существует, он должен сопоставляться с чем-то, и пользователь должен иметь возможность установить, что это такое.

Эта последовательность (и, вероятно, историческая случайность) приводит к ситуации, которую вы найдете.

Майкл Гомер
источник
Можете ли вы привести (надеюсь, в реальном мире) пример второго параграфа?
Муру
Осторожнее с аналогией с argv[0]. $0устанавливается только для argv[0]переданного, execve()когда это как shили bash. Для сценариев $0задается путь, заданный в качестве аргумента 1, 2 ... для интерпретатора (а для сценариев, выполняемых напрямую, который идет от первого аргумента пути к execve ()).
Стефан Шазелас