Jaki jest ostatni argument poprzedniego polecenia?

12

$_ jest uważany za ostatni argument poprzedniego polecenia.

Zastanawiam się więc, dlaczego tak nie jest, EDITOR="emacs -nw"ale EDITORw poniższym przykładzie?

Dlaczego nie jest "emacs -nw"częścią ostatniego argumentu?

Mówiąc bardziej ogólnie, jakie są definicje argumentu i ostatni argument?

Dzięki.

$ export EDITOR="emacs -nw"
$ echo $_
EDITOR
Tim
źródło
3
Myślę, że z tego samego powodu shellcheck mówi ci, żebyś nie eksportował zmiennych w tym samym wierszu, który im przypisujesz. Przypisanie następuje, a następnie zmienna jest eksportowana. EDITORjest argumentem do eksportu
jesse_b
FWIW pdkshi dashbędzie zawierać przypisaną wartość, ale ksh93będzie się zachowywał tak, jak bashrobi.
Kusalananda
zsh:, export FOO=bar; echo $_drukuje export.
ilkkachu
@Jesse_b Całość jest ostatnim argumentem / operandem (w tym przypisaną wartością), ale może mieć coś wspólnego z faktem, że exportjest wbudowanym narzędziem.
Kusalananda
ksh: typeset -x FOO=barnastępnie echo $_drukuje FOO, ale w declare -x FOO=bar; echo $_drukach Bash FOO=bar.
ilkkachu

Odpowiedzi:

13

Bash przetwarza zmienne zadania, gdy są one dopuszczone jako argumenty (z alias, declare, export, local, readonly, i typeset), zanim cokolwiek innego (albo raczej, identyfikuje je, zanim cokolwiek innego - rozbudowa dotyczy wartości przypisanych do zmiennych). Kiedy dochodzi do rozwinięcia słowa, pozostałe polecenie jest export EDITOR, więc _jest ustawione na EDITOR.

Ogólnie rzecz biorąc, argumentami są „słowa” pozostałe po rozwinięciu (które nie obejmują przypisań zmiennych i przekierowań).

Aby uzyskać szczegółowe informacje, zobacz Proste rozszerzanie poleceń w podręczniku Bash.

Stephen Kitt
źródło
I zdaję sobie sprawę declare, że zachowanie nie pasuje do tego, co opisuję ...
Stephen Kitt
Cóż, nie jest to zbyt konsekwentne. declare a=b; echo $_odciski a=b; export c=d; echo $_drukuje tylko c. aliaswydaje się wypisywać tylko nazwę, localz drugiej strony wypisuje cały argument. A readonlytakże drukuje tylko nazwę, co było dla mnie trochę zaskakujące, ponieważ myślałam readonlyi localbyłaby podobna do declare.
ilkkachu
1
@ilkkachu heh, zdałem sobie z tego sprawę (patrz wyżej). exporti readonlysą zadeklarowane razem w setattr.def, declare, local, i typesetsą zadeklarowane declare.def, aliasstoi samotnie alias.def.
Stephen Kitt
Dzięki. Kiedy przypisania zmiennych są używane jako argumenty dla niektórych poleceń, (1) „(a raczej, identyfikuje je zanim cokolwiek innego - interpretacja dotyczy wartości przypisanych do zmiennych)”, czy masz na myśli, że interpretacja dzieje się z wartościami przed wykonaniem przypisania zmiennej? (2) „Jeśli chodzi o rozwinięcie słowa, pozostałe polecenie to EDYTOR eksportu”, czy masz na myśli, że wykonanie przypisania zmiennej następuje przed rozwinięciem? Te dwa cytaty wydają się ze sobą sprzeczne.
Tim
Dzięki. Jestem trochę zmieszany. Gdy przypisania zmiennych są używane jako argumenty do alias, declare, export, local, readonlyi typeset. Co się stało pierwsze i następne? „Kiedy dochodzi do rozwinięcia słowa, pozostałe polecenie to export EDITOR”, czy sugerujesz, że przypisanie zmiennych EDITOR="emacs -nw"następuje przed rozwinięciem? Jeśli nie, dlaczego pozostałe polecenie nie zawiera przypisania jako argumentu? Jeśli tak, to czy rozszerzenie wartości przypisanych zmiennym nie musi nastąpić przed wykonaniem przypisania zmiennej?
Tim
4

TL; DR: W przypadku export FOO=barbash wywołuje tymczasowe tworzenie środowiska, ustawia się FOO=barw tym środowisku, a następnie wydaje polecenie końcowe export FOO. W tym momencie FOOjest uważany za ostatni argument.


Ach, nadużywane $_:

($ _, znak podkreślenia.) Przy uruchamianiu powłoki ustaw bezwzględną nazwę ścieżki używaną do wywoływania powłoki lub skryptu powłoki wykonywanej tak, jak przekazano w środowisku lub liście argumentów. Następnie rozwija się do ostatniego argumentu do poprzedniego polecenia, po rozwinięciu. Ustawiono również na pełną ścieżkę używaną do wywoływania każdego wykonanego polecenia i umieszczanego w środowisku eksportowanym do tego polecenia. Podczas sprawdzania poczty ten parametr przechowuje nazwę pliku poczty.

Spójrzmy na kilka odmian:

$ 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

Widzimy tutaj trzy wzory:

  • Polecenia wywoływane z systemu plików, funkcji i wbudowanych funkcji działają zgodnie z ogólnymi oczekiwaniami: $_w przypadku braku argumentów ustawiana jest sama nazwa polecenia, w przeciwnym razie ostatni z przedstawionych argumentów.
  • Po definicjach funkcji, pętlach i innych konstrukcjach logicznych: $_nie jest modyfikowany.
  • Wszystko inne: $_jest ustawione na coś, czego nie do końca się spodziewam; dziwne.

Oprzyrządowałem kod, aby zapewnić wgląd w dziwność.

$ ./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]

Możesz zobaczyć, że parser widzi oczekiwany ostatni argument ( lastarg=) we wszystkich przypadkach, ale to, co stanie się później, zależy od tego, co według bash powinno się wydarzyć. Zobacz execute_cmd.c, execute_simple_command () .

W przypadku export FOO=barbash dokonuje przypisania, a następnie eksportuje zmienną. Wydaje się to spójne z twierdzeniem dokumentacji, że ostatni argument został obliczony po rozwinięciu.

biskup
źródło
1
Skąd powłoka wie, że sprawdzasz pocztę?
rackandboneman
@rackandboneman bez potwierdzenia, podejrzewam, że kontrole wewnętrzne przeprowadzone na podstawieMAILCHECK
Jeff Schaller
2

Aby odpowiedzieć na pytanie tytułowe, spróbuj !$:

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

To jest ekspansja historii. Z strony podręcznika bash:

Rozszerzanie historii odbywa się natychmiast po odczytaniu pełnego wiersza, zanim powłoka podzieli go na słowa. Odbywa się to w dwóch częściach. Pierwszym jest określenie, który wiersz z listy historii ma zostać użyty podczas podstawiania. Drugim jest wybranie części tej linii do włączenia do bieżącej. Linia wybrana z historii jest zdarzeniem, a fragmenty tej linii, na które działają, to słowa.

...

Projektanci wydarzeń

...

! Rozpocznij podstawianie historii, z wyjątkiem sytuacji, gdy następuje po nich spacja, znak nowej linii, znak powrotu karetki, = lub ((gdy opcja powłoki extglob jest włączona przy użyciu wbudowanego shopt).

...

!! Zobacz poprzednie polecenie. To synonim `! -1 '.

...

Projektanci słów

...

$ Ostatnie słowo. Jest to zwykle ostatni argument, ale rozwija się do słowa zerowego, jeśli w wierszu jest tylko jedno słowo.

...

Jeśli oznaczenie słowa zostanie dostarczone bez specyfikacji zdarzenia, poprzednia komenda zostanie użyta jako zdarzenie.

JoL
źródło
Zbyt dosłownie bierzesz tytuł pytania. (OK, to kiepski tytuł.) Wszyscy widzimy, że polecenie « export EDITOR="emacs -nw"» składa się z dwóch słów: pierwsze to « export», a drugie « EDITOR="emacs -nw"». Pytanie naprawdę brzmi: „Co oznacza strona podręcznika bash i Podręcznik Bash, gdy mówią, że !_„ rozwija się do ostatniego argumentu do poprzedniego polecenia ”, biorąc pod uwagę, że w tym przypadku bash ustawia się $_na« EDITOR»?” Kopiowanie i wklejanie sekcji strony podręcznika bash na temat rozszerzania historii nie jest szczególnie pomocne.
G-Man mówi „Przywróć Monikę”