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як правило, не -sзадавати параметр з опцією трем ... Він , однак, може використовуватися багатьма тими ж способами 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. Oo
муру

2
@muru, що є правильним (і у вас, ймовірно, немає txtфайлів у поточному каталозі). Дивіться такожbash -c 'echo "${1?}"' foo
Stéphane Chazelas

Ага, так. Це практично корисний приклад.
muru

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_name [аргумент ...]

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

Що стосується того, чому ви хочете такої поведінки: це згладжує розрив між сценарієм та -cрядком. Ви можете безпосередньо конвертувати між ними без будь-якої зміни поведінки. Інші області покладаються на те, що вони однакові.

Це також узгоджується з тим, як працюють аргументи програми в цілому: це врешті-решт зводиться до виклику однієї з execфункцій, в якій є і перший наданий аргумент $0, і не менш часто цей аргумент є тим самим, що і виконуваний файл, який ви працюєте. Однак іноді ви хочете там особливу цінність, і іншого способу отримати це просто не було б. З огляду на те, що аргумент існує, він повинен щось відображати, і користувач повинен мати можливість встановити, що це таке.

Ця послідовність (і, ймовірно, історична аварія) призводить до ситуації, яку ви знайдете.


Чи можете ви навести (надіюсь, реальний) приклад другого пункту?
муру

Обережно з аналогією з argv[0]. $0встановлюється лише для того, щоб argv[0]передати, execve()коли це схоже на shабо bash. Для скриптів $0встановлюється шлях, заданий як аргумент 1, 2 ... до інтерпретатора (і для сценаріїв, що виконуються безпосередньо, що походить від першого аргументу шляху до execve ()).
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.