Який останній аргумент попередньої команди?


12

$_ кажуть, що це останній аргумент попередньої команди.

Тож мені цікаво, чому це не так, EDITOR="emacs -nw"як EDITORу наступному прикладі?

Чому не є "emacs -nw"частиною останнього аргументу?

Більш загально, що таке визначення аргументу та останній аргумент?

Спасибі.

$ export EDITOR="emacs -nw"
$ echo $_
EDITOR

3
Я думаю, що це з тієї ж причини, що shellcheck каже вам не експортувати змінні в той самий рядок, який ви їм призначили. Присвоєння відбувається, і тоді змінна експортується. EDITORє аргументом для експорту
jesse_b

FWIW, pdkshі dashбуде включати значення, яке було призначено, але ksh93буде вести себе так bashсамо.
Кусалаланда

zsh:, export FOO=bar; echo $_відбитки export.
ilkkachu

@Jesse_b Вся справа в останньому аргументі / операнді (включаючи призначене значення), але це може мати щось спільне з тим, що exportє вбудованою утилітою.
Кусалаланда

ksh: typeset -x FOO=barпотім echo $_друкує FOO, але в Bash declare -x FOO=bar; echo $_друкує FOO=bar.
ilkkachu

Відповіді:


13

Bash обробляє присвоєння змінних, коли вони допускаються в якості аргументів (з alias, declare, export, local, readonly, і typeset), перш ніж що - небудь (або , скоріше, він ідентифікує їх , перш ніж що - небудь інше - розширення відноситься до значень , призначені змінним). Коли доходить до розширення слів, інша команда є export EDITOR, так _встановлено EDITOR.

Взагалі кажучи, аргументи - це слова, що залишаються після розширення (що не включає призначення змінних і перенаправлення).

Докладні відомості див. У розділі Просте розширення команд у посібнику Bash.


І я усвідомлюю declare, що поведінка не відповідає тому, що я описую ...
Стівен Кітт

Що ж, це не дуже послідовно. declare a=b; echo $_відбитки a=b; export c=d; echo $_відбитки просто c. aliasздається, друкує лише ім'я, localз іншого боку друкує весь аргумент. А readonlyтакож друкує лише ім'я, яке мене трохи здивувало, оскільки я б подумав readonlyі localбув би схожий на нього declare.
ilkkachu

1
@ilkkachu хе, я зрозумів, що теж (див. вище). exportі readonlyоголошуються разом setattr.def, declare, localі typesetоголошені в declare.def, aliasстоїть осібно в alias.def.
Стівен Кітт

Спасибі. Коли присвоєння змінних використовуються як аргументи для деяких команд, (1) "(а точніше, він ідентифікує їх перед чим-небудь іншим - розширення застосовується до значень, присвоєних змінним)", ви маєте на увазі, що розширення відбувається зі значеннями перед виконанням призначення змінної? (2) "Якщо мова йде про розширення слів, що залишилася команда - експорт EDITOR", ви маєте на увазі виконання присвоєння змінних, що відбувається до розширення? Дві цитати, здається, суперечать один одному.
Тім

Спасибі. Я трохи розгублений. Коли присвоєння змінних використовуються як аргументи до alias, declare, export, local, readonlyта typeset. Що відбувається перше та наступне? "Коли мова йде про розширення слів, залишилася команда export EDITOR", ви маєте на увазі, що присвоєння змінної EDITOR="emacs -nw"відбувається перед розширенням? Якщо ні, то чому інша команда не містить призначення як аргумент? Якщо так, чи не повинно відбуватися розширення значень, присвоєних змінним, перш ніж виконувати призначення змінної?
Тим

4

TL; DR: У випадку export FOO=bar, коли bash посилається на своє тимчасове створення середовища, встановлює FOO=barв цьому середовищі, тоді отримує остаточну команду export FOO. У цей момент FOOприймається як останній аргумент.


Ах, зловживаних $_:

($ _, підкреслення.) При запуску оболонки встановіть абсолютне ім'я шляху, яке використовується для виклику сценарію оболонки або оболонки, який виконується, як передано в середовищі або списку аргументів. Згодом розширюється до останнього аргументу до попередньої команди, після розширення. Також встановіть повне ім'я шляху, яке використовується для виклику кожної команди, виконаної та розміщеної у середовищі, експортованому до цієї команди. Під час перевірки пошти цей параметр містить ім'я поштового файлу.

Розглянемо кілька варіантів:

$ man; echo $_
What manual page do you want?
man
$ man foo; echo $_
No manual entry for foo
foo
$ echo; echo $_

echo
$ echo bar foo; echo $_
bar foo
foo
$ foo=x eval 'echo $foo'; echo $_
x
echo $foo
$ bar() { man $1; }; echo $_
foo
$ for (( i=0; $i<0; i=i+1 )); do echo $i; done; echo $_
foo
$ bar; echo $_
What manual page do you want?
man
$ bar foo; echo $_
No manual entry for foo
foo
$ MANPATH=/tmp; echo $_

$ export MANPATH=/tmp; echo $_
MANPATH

Тож ми бачимо тут три закономірності:

  • Команди, викликані з файлової системи, функції та вбудовані модулі поводяться як зазвичай очікується: $_встановлюється саме ім'я команди, якщо немає аргументів, інакше останній із представлених аргументів.
  • Після визначення функцій, циклів та інших логічних конструкцій: $_не змінюється.
  • Все інше: $_встановлюється на щось не зовсім очікуване; дивно.

Я інструментував код, щоб дати деяке розуміння дивацтва.

$ ./bash --noprofile --norc -c 'man foo'
lastword=[man]
lastarg=[foo]
$ ./bash --noprofile --norc -c 'export FOO=bar'
lastword=[export]
lastarg=[FOO=bar]
bind_variable, name=[FOO], value=[bar]
before bind_lastarg, lastarg=[FOO]
bind_lastarg, arg=[FOO]
bind_variable, name=[_], value=[FOO]
$ ./bash --noprofile --norc -c 'declare FOO=bar'
lastword=[declare]
lastarg=[FOO=bar]
bind_variable, name=[FOO], value=[(null)]
before bind_lastarg, lastarg=[FOO=bar]
bind_lastarg, arg=[FOO=bar]
bind_variable, name=[_], value=[FOO=bar]

Ви можете бачити, що аналізатор бачить очікуваний останній аргумент ( lastarg=) у всіх випадках, але те, що станеться далі, залежить від того, що баш думає, що має відбутися. Див. Execute_cmd.c, Execute_simple_command () .

У випадку з export FOO=barbash робить призначення, а потім експортує змінну. Це здається узгодженим із твердженням документації, що останній аргумент обчислюється після розширення.


1
Як оболонка знає, що ви перевіряєте пошту?
rackandboneman

@rackandboneman не підтверджуючи, я підозрюю внутрішні перевірки, зроблені на основіMAILCHECK
Jeff Schaller

2

Щоб відповісти на заголовок, спробуйте !$:

$ export EDITOR="emacs -nw"
$ echo !$
EDITOR=emacs -nw

Це розширення історії. З сайту bash manpage:

Розширення історії виконується одразу після зчитування повного рядка, перш ніж оболонка розбиває його на слова. Він відбувається в двох частинах. Перший - визначити, який рядок зі списку історії використовувати під час заміни. Друга - вибрати частини цього рядка для включення в поточний. Рядок, вибраний з історії, - це подія, а частини цього рядка, на які діє, - це слова.

...

Дизайнери подій

...

! Почніть заміну історії, за винятком випадків, коли слідує порожній, новий рядок, повернення каретки, = або ((коли параметр оболонки extglob увімкнено за допомогою вбудованого входу).

...

!! Зверніться до попередньої команди. Це синонім `! -1 '.

...

Словознавці

...

$ Останнє слово. Зазвичай це останній аргумент, але він розшириться до нульового слова, якщо в рядку буде лише одне слово.

...

Якщо позначений слово подається без специфікації події, попередня команда використовується як подія.


Ви приймаєте назву питання waaaaay занадто буквально. (Гаразд, це поганий заголовок.) Усі ми можемо бачити, що команда « export EDITOR="emacs -nw"» складається з двох слів: перше - « export», а друге - « EDITOR="emacs -nw"». Питання справді задає питання: "Що означає сторінка bash man і Посібник Bash, коли вони говорять, що !_" розширюється на останній аргумент до попередньої команди ", враховуючи, що в цьому випадку bash встановлює $_значення" EDITOR"?" Копіювання та вклеювання розділу сторінки bash man на розширення історії не особливо корисне.
G-Man каже: "Відновіть Моніку"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.