Bash -c với các tham số vị trí


15

Thông thường, $0trong một tập lệnh được đặt thành tên của tập lệnh hoặc theo bất kỳ tập lệnh nào được gọi là (bao gồm cả đường dẫn). Tuy nhiên, nếu tôi sử dụng bashvới -ctùy chọn, $0được đặt thành đầu tiên của các đối số được truyền sau chuỗi lệnh:

bash -c 'echo $0 ' foo bar 
# foo 

Trong thực tế, có vẻ như các tham số vị trí đã được thay đổi, nhưng bao gồm $0. Tuy nhiên, shifttrong chuỗi lệnh không ảnh hưởng $0(như bình thường):

bash -c 'echo $0; shift; echo $0 ' foo bar
# foo
# foo

Tại sao điều này rõ ràng hành vi kỳ lạ cho chuỗi lệnh? Lưu ý rằng tôi đang tìm kiếm lý do, lý do, đằng sau việc thực hiện hành vi kỳ quặc như vậy.


Người ta có thể suy đoán rằng một chuỗi lệnh như vậy sẽ không cần $0tham số như thường được xác định, vì vậy để tiết kiệm, nó cũng được sử dụng cho các đối số bình thường. Tuy nhiên, trong trường hợp đó hành vi của shiftlà kỳ quặc. Một khả năng khác là $0được sử dụng để xác định hành vi của các chương trình (a la bashđược gọi là shhoặc vimđược gọi là vi), nhưng điều đó là không thể, vì $0ở đây chỉ được nhìn thấy trong chuỗi lệnh chứ không phải bởi các chương trình được gọi trong nó. Tôi không thể nghĩ ra bất kỳ cách sử dụng nào khác $0, vì vậy tôi không thể giải thích điều này.


Là một lưu ý phụ, tôi đã thấy việc sử dụng -cho $0đối số như một thành ngữ, như trong sh -c 'foo $1 $2' - a b. Cách này trông khá bình thường (một khi bạn phát hiện ra điều đó -có nghĩa là gì)
Volker Siegel

echo 'echo the other side of this pipe globs "$@"' | sh -s -- *Tuy nhiên, bạn cũng có thể , không may, $0nói chung không phải là một tham số có thể thiết lập được với -stùy chọn tream ... Mặc dù vậy, nó có thể được sử dụng theo nhiều cách tương tự xargs. Và những người khác bên cạnh.
mikeerv

@VolkerSiegel Bình thường hơn --, sau đó có thể có cách giải thích thông thường là 'từ đây bắt đầu các cuộc tranh luận', được thấy trong một số chương trình khác. Sau đó, một lần nữa, điều đó có thể gây nhầm lẫn cho những người không quen -cnghĩ rằng --thực sự có cách giải thích đó.
muru

Câu trả lời:


10

Điều đó cho bạn cơ hội để đặt / chọn $0khi sử dụng tập lệnh nội tuyến. Nếu không, $0sẽ chỉ là bash.

Sau đó, bạn có thể làm ví dụ:

$ 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

Không phải tất cả các vỏ được sử dụng để làm điều đó. Vỏ Bourne đã làm. Shell Korn (và Almquist) đã chọn để có tham số đầu tiên $1thay thế. POSIX cuối cùng đã đi theo con đường Bourne, vì vậy kshashcác công cụ phái sinh trở lại sau đó (nhiều hơn về điều đó tại http://www.in-ulm.de/~mascheck/various/find/#shell ). Điều đó có nghĩa là trong một thời gian dài sh(tùy thuộc vào hệ thống được dựa trên vỏ Bourne, Almquist hoặc Korn), bạn không biết liệu cuộc tranh luận đầu tiên có xảy ra $0hay không $1, vì vậy, đối với tính di động, bạn phải thực hiện những việc như:

sh -c 'echo foo in "$1"' foo foo

Hoặc là:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

Rất may, POSIX đã chỉ định hành vi mới trong đó đối số đầu tiên xuất hiện $0, vì vậy bây giờ chúng ta có thể thực hiện một cách hợp lý:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt

Tôi mới chạy: bash -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txttrên Ubuntu 14.04, bash version 4.3.11(1)-releasevà tôi đã nhận được : txt files are *.txt. Oo
muru

2
@muru, đúng (và có thể bạn không có txttệp trong thư mục hiện tại). Xem thêmbash -c 'echo "${1?}"' foo
Stéphane Chazelas

À, vâng. Đó là một ví dụ thực tế hữu ích.
muru

1
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txtlà tuyệt vời sáng tạo!
iruvar

Hoặc bạn sử dụng một cách giải quyết hoàn toàn mạnh mẽ (được đề xuất bởi Stéphane Chazelas trong comp.unix.shell) SHELL -c 'shift $1; command' 2 1 arg1 arg2 ... haha ...
mikeerv

3

Hành vi này được xác định bởi POSIX :

sh -c lệnh_name [đối số ...]

Đọc các lệnh từ toán hạng Command_ chuỗi. Đặt giá trị của tham số đặc biệt 0 (xem Tham số đặc biệt ) từ giá trị của toán hạng Command_name và các tham số vị trí ($ 1, $ 2, v.v.) theo thứ tự từ các toán hạng đối số còn lại.

Về lý do tại sao bạn muốn hành vi đó: điều này giúp xóa tan khoảng cách giữa tập lệnh và -cchuỗi. Bạn có thể chuyển đổi trực tiếp giữa hai người mà không có bất kỳ thay đổi hành vi nào. Các khu vực khác dựa trên những điều này là giống hệt nhau.

Nó cũng phù hợp với cách thức hoạt động của các đối số chương trình nói chung: điều này cuối cùng bắt nguồn từ việc gọi một trong các exechàm, trong đó đối số được cung cấp đầu tiên cũng vậy $0, và thông thường, đối số đó cũng giống như đối số mà bạn đang chạy. Tuy nhiên, đôi khi, bạn muốn có một giá trị đặc biệt ở đó, và không có cách nào khác để có được nó. Cho rằng đối số tồn tại, nó phải ánh xạ tới một cái gì đó và người dùng cần có khả năng thiết lập đó là gì.

Sự nhất quán này (và có khả năng là tai nạn lịch sử) dẫn đến tình huống bạn tìm thấy.


Bạn có thể đưa ra một ví dụ (hy vọng trong thế giới thực) của đoạn thứ hai không?
muru

Cẩn thận với sự tương tự với argv[0]. $0chỉ được đặt thành argv[0]thông qua execve()khi nó thích shhoặc bash. Đối với tập lệnh, $0được đặt thành đường dẫn được cung cấp dưới dạng đối số 1, 2 ... cho trình thông dịch (và đối với tập lệnh được thực thi trực tiếp, xuất phát từ đối số đường dẫn đầu tiên đến execve ()).
Stéphane Chazelas
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.