Làm thế nào để shebang này bắt đầu với một dấu gạch nối kép (-) hoạt động?


14

Tôi đã tìm thấy loại shebang sau trong trang RosettaCode:

--() { :; }; exec db2 -txf "$0"

Nó hoạt động cho Db2, và một điều tương tự cho Postgres. Tuy nhiên, tôi không hiểu toàn bộ dòng.

Tôi biết dấu gạch ngang kép là một nhận xét trong SQL và sau đó nó gọi Db2 có thể thực thi được với một số tham số truyền tệp chính nó dưới dạng tệp. Nhưng những gì về dấu ngoặc đơn, các chữ nổi xoăn, dấu hai chấm và dấu hai chấm và làm thế nào có thể thay thế một shebang thực sự! ?

https://rosettacode.org/wiki/Multiline_shebang#PostgreQuery

Câu trả lời:


18

Liên quan: Trình thông dịch shell nào chạy tập lệnh không có shebang?

Kịch bản không có shebang / hashbang / #!line, đơn giản là vì dấu gạch ngang kép không có #!.

Tuy nhiên, tập lệnh sẽ được thực thi bởi shell (xem câu hỏi và câu trả lời được liên kết ở trên) và trong shell đó, nếu -là một ký tự hợp lệ trong tên hàm, dòng này khai báo một hàm shell gọi là --không có gì (tốt, nó chạy :, mà không có gì ) và không bao giờ được gọi.

Hàm này, trong ký hiệu nhiều dòng phổ biến hơn (chỉ để làm cho nó rõ ràng hơn trông như thế nào, vì tên kỳ lạ của nó che khuất thực tế rằng nó thực sự là một hàm):

-- () {
  :
}

Mục đích duy nhất của định nghĩa hàm là có một dòng hợp lệ trong tập lệnh shell và đồng thời là một lệnh SQL hợp lệ (một nhận xét). Loại mã này được gọi là một polyglot .

Sau khi khai báo hàm shell không có thật, tập lệnh, khi được trình thông dịch shell shell thực thi, sử dụng execđể thay thế trình bao hiện tại bằng quy trình do chạy db2 -txf "$0", sẽ giống như sử dụng db2 -txftên đường dẫn của tập lệnh từ dòng lệnh.

Thủ thuật này có thể sẽ không hoạt động đáng tin cậy trên các hệ thống trong đó dashhoặc các ashvỏ dựa trên khác yash, vỏ Bourne ksh88hoặc ksh93được sử dụng như /bin/shlà vì các vỏ này không chấp nhận các hàm có tên chứa dấu gạch ngang.

Cũng liên quan:


Tôi cho rằng những điều sau đây cũng sẽ hoạt động (không thực sự được thử nghiệm):

--() { exec db2 -txf "$0"; }; --

@ilkkachu Bây giờ tốt hơn chưa?
Kusalananda

1
ồ vâng Và cảm ơn vì đã nhắc nhở tôi những thứ đó được gọi là gì. :)
ilkkachu

6

Như @Kusalananda đã nói, mánh khóe đó đã bị phá vỡ và nó sẽ không hoạt động trong tất cả các vỏ.

Đây là nỗ lực của tôi để làm điều đó một cách hợp lý:

--/.. 2>/dev/null; exec db2 -txf "$0"

Lệnh đầu tiên sẽ thất bại ngay cả khi một tệp / thư mục có tên --tồn tại trong thư mục hiện tại và bất kỳ lỗi nào sẽ bị tắt bởi 2>/dev/null; Shell sau đó sẽ tiến hành lệnh thứ hai , exec.


Nó vẫn chưa thực sự di động. Đây không phải là tập lệnh hợp lệ và bạn vẫn đang dựa vào trình gọi để giải quyết vấn đề là kernel sẽ từ chối chạy tập lệnh và trả về ENOEXECnếu bạn cố gắng. Hãy thử chạy tập lệnh bên dưới straceđể xem ý tôi là gì.
kasperd

@kasperd, nó vẫn phải là xách tay, shell được cho là chạy tập lệnh dưới dạng tập lệnh shell nếu exec()không hoạt động trên nó. "Nếu hàm execl () không thành công do lỗi tương đương với lỗi [ENOEXEC], shell sẽ thực thi một lệnh tương đương với việc shell được gọi với tên lệnh là toán hạng đầu tiên của nó, ..." (xem pubs.opengroup .org / onlinepub / 9699919799.2018edition / tiện ích / Cách )
ilkkachu

@ilkkachu Nhưng các tập lệnh không phải lúc nào cũng được thực thi từ trình bao. Nếu bạn cố gắng sử dụng tập lệnh trong bất kỳ bối cảnh nào khác nơi thực thi sẽ hoạt động thì nó sẽ thất bại. Hơn nữa, shell không đồng ý sử dụng trình thông dịch nào. Vì vậy, tập lệnh của bạn bây giờ sẽ hành xử khác đi hoặc thất bại hoàn toàn tùy thuộc vào bối cảnh mà nó được gọi từ đâu.
kasperd

@kasperd, tốt, chắc chắn, nó sẽ không hoạt động nếu bạn exec()trực tiếp từ một thứ khác ngoài vỏ. Nhưng trường hợp đó sẽ là gì? Bạn có thể muốn chạy tập lệnh từ cronhoặc như vậy, nhưng tôi nghĩ dù sao nó cũng chạy mọi thứ thông qua trình bao, và thậm chí nếu không, thật dễ dàng để đánh vần db2 -txf /path/to/scripttrong trường hợp đó, vì bạn chỉ cần thực hiện một lần. Có công việc tốc ký chủ yếu là hữu ích trên một vỏ tương tác. Nhưng chắc chắn, một tập lệnh bao bọc riêng biệt có thể mạnh mẽ hơn.
ilkkachu

1
@kasperd Tôi sẽ không làm phiền bạn với các tài liệu và tiêu chuẩn; hãy thử nó! echo 'int main(int c,char**a){execvp(a[1],a+1);}' | cc -include unistd.h -xc -; echo echo yeah > a.sh; chmod 755 a.sh; ./a.out ./a.sh; PATH=`pwd` ./a.out a.sh
Chú Billy
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.