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.