Đối số cuối cùng của lệnh trước là gì?


12

$_ được cho là đối số cuối cùng của lệnh trước đó.

Vì vậy, tôi tự hỏi tại sao nó không EDITOR="emacs -nw"nhưng EDITORtrong ví dụ sau đây?

Tại sao không phải là "emacs -nw"một phần của cuộc tranh luận cuối cùng?

Tổng quát hơn, các định nghĩa của một đối số và đối số cuối cùng là gì?

Cảm ơn.

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

3
Tôi nghĩ rằng vì lý do tương tự mà shellcheck bảo bạn không xuất các biến trên cùng một dòng bạn gán chúng. Việc gán xảy ra và sau đó biến được xuất. EDITORlà một đối số để xuất
jesse_b

FWIW, pdkshdashsẽ bao gồm giá trị đã được chỉ định, nhưng ksh93sẽ hành xử như bashvậy.
Kusalananda

zsh : export FOO=bar; echo $_, in export.
ilkkachu

@Jesse_b Toàn bộ điều là đối số / toán hạng cuối cùng (bao gồm cả giá trị được gán), nhưng nó có thể có liên quan đến thực tế exportlà một tiện ích tích hợp.
Kusalananda

ksh: typeset -x FOO=barsau đó echo $_in FOO, nhưng trong Bash declare -x FOO=bar; echo $_in FOO=bar.
ilkkachu

Câu trả lời:


13

Bash xử lý các bài tập biến, khi họ đang cho phép như các đối số (với alias, declare, export, local, readonly, và typeset), trước khi bất cứ thứ gì (hay đúng hơn, nó xác định chúng trước khi bất cứ điều gì khác - mở rộng áp dụng đối với các giá trị được gán cho biến). Khi nó được mở rộng từ, lệnh còn lại là export EDITOR, vì vậy _được đặt thành EDITOR.

Nói chung, các đối số là các từ tiếng Việt còn lại sau khi mở rộng (không bao gồm các phép gán và chuyển hướng biến).

Xem Mở rộng lệnh đơn giản trong hướng dẫn Bash để biết chi tiết.


Và tôi nhận ra declarehành vi của mình không phù hợp với những gì tôi mô tả ...
Stephen Kitt

Vâng, nó không nhất quán trong đó. declare a=b; echo $_bản in a=b; export c=d; echo $_chỉ in c. aliasdường như chỉ in tên, localmặt khác in toàn bộ đối số. Và readonlycũng chỉ in tên, điều mà tôi thấy hơi ngạc nhiên vì tôi đã nghĩ readonlylocalsẽ giống như vậy declare.
ilkkachu

1
@ilkkachu heh, tôi cũng nhận ra điều đó (xem ở trên). exportreadonlyđược khai báo với nhau trong setattr.def, declare, local, và typesetđược khai báo trong declare.def, aliasđứng một mình ở alias.def.
Stephen Kitt

Cảm ơn. Khi các phép gán biến được sử dụng làm đối số cho một số lệnh, (1) "(hay đúng hơn, nó xác định chúng trước bất kỳ thứ gì khác - mở rộng áp dụng cho các giá trị được gán cho biến)", bạn có nghĩa là mở rộng xảy ra với các giá trị trước khi thực hiện gán biến? (2) "Khi mở rộng từ, lệnh còn lại là xuất EDITOR", bạn có nghĩa là thực hiện gán biến xảy ra trước khi mở rộng? Hai trích dẫn dường như mâu thuẫn với nhau.
Tim

Cảm ơn. Tôi hơi bối rối. Khi các phép gán biến được sử dụng làm đối số cho alias, declare, export, local, readonlytypeset. Điều gì xảy ra đầu tiên và tiếp theo? "Khi mở rộng từ, lệnh còn lại là export EDITOR", bạn có ngụ ý rằng việc gán biến EDITOR="emacs -nw"xảy ra trước khi mở rộng không? Nếu không, tại sao lệnh còn lại không chứa phép gán làm đối số? Nếu có, không mở rộng các giá trị được gán cho các biến phải xảy ra trước khi thực hiện gán biến?
Tim

4

TL; DR: Trong trường hợp export FOO=bar, bash gọi việc tạo môi trường tạm thời của nó, đặt FOO=bartrong môi trường đó, sau đó đưa ra lệnh cuối cùng là export FOO. Tại thời điểm đó, FOOđược coi là đối số cuối cùng.


Ah, bị lạm dụng nhiều $_:

($ _, một dấu gạch dưới.) Khi khởi động shell, được đặt thành tên đường dẫn tuyệt đối được sử dụng để gọi shell hoặc shell script đang được thực thi như được truyền trong môi trường hoặc danh sách đối số. Sau đó, mở rộng đối số cuối cùng sang lệnh trước đó, sau khi mở rộng. Cũng được đặt thành tên đường dẫn đầy đủ được sử dụng để gọi từng lệnh được thực thi và được đặt trong môi trường được xuất sang lệnh đó. Khi kiểm tra thư, tham số này giữ tên của tệp thư.

Hãy xem xét một vài biến thể:

$ 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

Vì vậy, chúng tôi thấy ba mẫu ở đây:

  • Các lệnh được gọi từ hệ thống tập tin, các hàm và các hàm dựng sẵn hoạt động như thường thấy: $_được đặt thành tên lệnh nếu không có đối số, nếu không thì là đối số cuối cùng được trình bày.
  • Sau khi định nghĩa hàm, các vòng lặp và các cấu trúc logic khác: $_không được sửa đổi.
  • Mọi thứ khác: $_được đặt thành một cái gì đó không hoàn toàn mong đợi; kỳ dị.

Tôi đã sử dụng mã để cung cấp một số cái nhìn sâu sắc về sự kỳ lạ.

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

Bạn có thể thấy rằng trình phân tích cú pháp nhìn thấy đối số cuối cùng dự kiến ​​( lastarg=) trong mọi trường hợp, nhưng những gì xảy ra sau đó phụ thuộc vào những gì bash nghĩ sẽ xảy ra. Xem exec_cmd.c, exec_simple_command () .

Trong trường hợp export FOO=bar, bash thực hiện chuyển nhượng và sau đó xuất biến. Điều này có vẻ phù hợp với khẳng định của tài liệu rằng đối số cuối cùng được tính sau khi mở rộng.


1
Làm thế nào để shell biết bạn đang kiểm tra thư?
rackandboneman

@rackandboneman mà không xác nhận, tôi nghi ngờ kiểm tra nội bộ được thực hiện dựa trênMAILCHECK
Jeff Schaller

2

Để trả lời câu hỏi tiêu đề, hãy thử !$:

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

Đây là mở rộng lịch sử. Từ trang bash:

Mở rộng lịch sử được thực hiện ngay sau khi một dòng hoàn chỉnh được đọc, trước khi trình bao phá vỡ nó thành từ. Nó diễn ra trong hai phần. Đầu tiên là xác định dòng nào từ danh sách lịch sử sẽ sử dụng trong quá trình thay thế. Thứ hai là chọn các phần của dòng đó để đưa vào phần hiện tại. Dòng được chọn từ lịch sử là sự kiện và các phần của dòng đó được tác động là từ.

...

Nhà thiết kế sự kiện

...

! Bắt đầu thay thế lịch sử, ngoại trừ khi được theo sau bởi một dòng trống, dòng mới, trả lại vận chuyển, = hoặc ((khi tùy chọn vỏ extglob được bật bằng cách sử dụng nội dung shopt).

...

!! Tham khảo lệnh trước. Đây là một từ đồng nghĩa với `! -1 '.

...

Trình thiết kế từ

...

$ Từ cuối cùng. Đây thường là đối số cuối cùng, nhưng sẽ mở rộng sang từ zeroth nếu chỉ có một từ trong dòng.

...

Nếu một từ chỉ định được cung cấp mà không có đặc tả sự kiện, lệnh trước đó được sử dụng làm sự kiện.


Bạn đang lấy tiêu đề của câu hỏi waaaaay theo đúng nghĩa đen. (OK, đó là một tiêu đề kém.) Tất cả chúng ta đều có thể thấy rằng lệnh « export EDITOR="emacs -nw"» bao gồm hai từ: đầu tiên là « export» và thứ hai là « EDITOR="emacs -nw"». Câu hỏi thực sự là hỏi, Trang bash man và Sổ tay Bash nghĩa là gì khi họ nói rằng !_'mở rộng đối số cuối cùng cho lệnh trước', cho rằng bash đặt $_thành « EDITOR» trong trường hợp này? Sao chép và dán phần bash man trên phần mở rộng lịch sử không đặc biệt hữu ích.
G-Man nói 'Phục hồi Monica'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.