7

Có rất nhiều goodreasons để sử dụng #!/usr/bin/env. Tóm lại: Nó làm cho mã của bạn dễ dàng hơn. Vâng, sorta. Check this out ....#!/usr/bin/env và tên quá trình: tính di động ở mức giá?


Tôi có hai kịch bản gần như giống hệt, bintest.py

#! /usr/bin/python 
import time 
time.sleep(5*60) 

envtest.py

#! /usr/bin/env python 
import time 
time.sleep(5*60) 

Lưu ý rằng chúng chỉ khác nhau ở shebangs của họ.


bintest.py chạy như mong đợi

 
[email protected]:~$ ./bintest.py & ps && killall bintest.py 
[1] 15061 
    PID TTY   TIME CMD 
14625 pts/0 00:00:00 bash 
15061 pts/0 00:00:00 bintest.py 
15062 pts/0 00:00:00 ps 
[email protected]:~$ 
[1]+ Terminated    ./bintest.py 

nhưng envtest.py làm điều gì đó ít hơn tối ưu

 
[email protected]:~$ ./envtest.py & ps && killall envtest.py 
[1] 15066 
    PID TTY   TIME CMD 
14625 pts/0 00:00:00 bash 
15066 pts/0 00:00:00 python 
15067 pts/0 00:00:00 ps 
envtest.py: no process found 
[email protected]:~$ killall python 
[email protected]:~$ 
[1]+ Terminated    ./envtest.py 

gì chúng ta đã thấy là sử dụng #! /usr/bin/env gây ra quá trình này để nhận tên "python" thay vì "envtest.py", do đó hiển thị killall của chúng tôi không hiệu quả. Ở một mức độ nào đó, có vẻ như chúng ta đã giao dịch một loại tính di động cho một loại khác: bây giờ chúng ta có thể hoán đổi các thông dịch viên trăn một cách dễ dàng, nhưng chúng ta đã mất "khả năng giết" trên dòng lệnh. Có chuyện gì thế? Nếu có một thực hành tốt nhất ở đây để đạt được cả hai, nó là gì?

+0

Ngoài ra còn có một số lý do tốt * không * sử dụng '#/usr/bin/env'!; xem [câu hỏi này] (http://unix.stackexchange.com/q/29608/10454) và [câu trả lời của tôi] (http://unix.stackexchange.com/a/29620/10454). –

+0

@KeithThompson, lý do tôi đặt tiền thưởng vào việc này là vì tôi đang làm việc trên một tập lệnh cần chạy trên cả Linux và Mac OS X, và họ đặt tệp thực thi cần thiết ở các vị trí khác nhau trong $ PATH, nhưng Tôi vẫn muốn '' top'' liệt kê từng chữ "chính xác" để bạn có thể phân biệt một tập lệnh với tập lệnh khác. –

+0

Sẽ không phải là giải pháp để sử dụng kịch bản cài đặt thay thế trình thông dịch cục bộ chính xác trên dòng shebang? – tripleee

Trả lời

3

"kill-ability" trên dòng lệnh có thể được giải quyết một cách ổn định và đáng tin cậy bằng cách sử dụng PID của quá trình nền lấy từ biến số $!.

$ ./bintest.py & bg_pid=$! ; echo bg_pid=$bg_pid ; ps && kill $bg_pid 
[1] 2993 
bg_pid=2993 
    PID TTY   TIME CMD 
2410 pts/0 00:00:00 bash 
2993 pts/0 00:00:00 bintest.py 
2994 pts/0 00:00:00 ps 
$ 
[1]+ Terminated    ./bintest.py 
$ 

và envtest.py

$ ./envtest.py & bg_pid=$! ; echo bg_pid=$bg_pid ; ps && kill $bg_pid 
[1] 3016 
bg_pid=3016 
    PID TTY   TIME CMD 
2410 pts/0 00:00:00 bash 
3016 pts/0 00:00:00 python 
3017 pts/0 00:00:00 ps 
$ 
[1]+ Terminated    ./envtest.py 
$ 

Như @ Adam Bryzak chỉ ra, không phải kịch bản gây ra tiêu đề quá trình được thiết lập trên Mac OS X. Vì vậy, nếu tính năng đó là một yêu cầu công ty, bạn có thể cần cài đặt và sử dụng mô-đun python setproctitle cùng với đơn đăng ký của bạn.

bài Stackoverflow này thảo luận setting process title in python

0

Tôi không nghĩ rằng bạn có thể dựa vào số killall bằng cách sử dụng tên tập lệnh để hoạt động mọi lúc. Trên Mac OS XI nhận được đầu ra sau đây từ ps sau khi chạy cả hai kịch bản:

2108 ttys004 0:00.04 /usr/local/bin/python /Users/adam/bin/bintest.py 
2133 ttys004 0:00.03 python /Users/adam/bin/envtest.py 

và chạy killall bintest.py kết quả trong

No matching processes belonging to you were found 
+0

Nội bộ của 'ps' và' killall' khác nhau giữa các hệ thống, do đó, một phần của vấn đề đang sử dụng 'killall' làm tiêu chí cho những lời chỉ trích. Câu hỏi thú vị, mặc dù. – tripleee

0

Trong khi tôi sẽ vẫn giống như một giải pháp mà làm cho ngôn ngữ kịch bản cả hai nền tảng và dễ giám sát từ dòng lệnh, nếu bạn chỉ đang tìm kiếm phương án thay thế cho killall <scriptname> để ngừng dịch vụ tùy chỉnh, dưới đây là cách tôi giải quyết:

kill `ps -fC <interpreterName> | sed -n '/<scriptName>/s/^[^0-9]*\([0-9]*\).*$/\1/gp'` 

Đối với những người không quá quen thuộc với ps và regexes, ps 's -f sửa đổi nó liệt kê ra một bộ "đầy đủ" thông tin về một quá trình, bao gồm các đối số dòng lệnh của nó, và -C nói với nó để lọc danh sách để chỉ lệnh khớp với đối số dòng lệnh tiếp theo. Thay thế <interpreterName> bằng python hoặc node hoặc bất kỳ thứ gì. Các đối số

sed 's -n cho phép nó không in bất cứ điều gì theo mặc định và tập lệnh regex phải chỉ rõ rằng bạn muốn in một cái gì đó.

Trong regex, /<scriptName>/ đầu tiên yêu cầu bộ lọc lọc kết quả của nó thành chỉ các dòng chứa regex nội thất. Ví dụ: bạn có thể thay thế <scriptName> bằng envtest.

s cho biết rằng regex thay thế sẽ theo sau. /^[^0-9]*\([0-9]*\).*$/ là phần đối sánh dòng và /\1/ là phần thay thế. Trong phần khớp dòng, ^ ngay từ đầu và $ ở cuối cùng có nghĩa là khớp phải bắt đầu từ đầu dòng và kết thúc ở cuối dòng - toàn bộ dòng đang được chọn sẽ được thay thế .

[^0-9]* liên quan đến một số việc: [] được sử dụng để xác định tập hợp các ký tự cho phép. Trong phần này của regex, dấu gạch ngang - có nghĩa là một loạt các ký tự, do đó, nó mở rộng thành . ^ ở đây có nghĩa là "không" và ngay lập tức có nghĩa là "khớp với bất kỳ ký tự nào KHÔNG phải là số". Dấu hoa thị * sau đó có nghĩa là giữ các ký tự trùng khớp trong tập hợp này cho đến khi nó gặp một ký tự không khớp, trong trường hợp này là một số.

\([0-9]*\) có hai phần, \(\)[0-9]*. Sau này nên dễ dàng làm theo từ lời giải thích trước đó: nó chỉ phù hợp với số, và lấy càng nhiều càng tốt. Các \(\) có nghĩa là để lưu nội dung của những gì được kết hợp với một biến tạm thời. (Trong các phiên bản RegEx khác, bao gồm javascript và Perl, () được sử dụng, thay thế.)

Cuối cùng, .* nghĩa để phù hợp với tất cả các nhân vật còn lại, như . nghĩa là bất kỳ nhân vật càng tốt.

Phần /\1/ nói để thay thế phần khớp của dòng (là toàn bộ dòng trong trường hợp này) với \1, tham chiếu đến biến tạm thời đã lưu (nếu có hai phần \(\), phần đầu tiên trong RegEx sẽ là \1 và số thứ hai \2).

g sau đó có nghĩa là "tham lam" và chạy mã khớp này trên mọi dòng gặp phải và p có nghĩa là in bất kỳ dòng nào đã đạt đến điểm này.

Về mặt kỹ thuật, điều này sẽ nổ tung nếu bạn có nhiều bản sao của kịch bản của bạn chạy, và bạn muốn thực sự muốn hơi nặng:

ps -fC <interpreterName> | sed -n '/<scriptName>/s/^[^0-9]*\([0-9]*\).$/kill \1/gp' | bash 

Nếu bạn muốn thực sự tái tạo kill * tất cả * Chức năng, nhưng điều này sẽ sinh ra một bash shell riêng biệt cho mỗi kịch bản mà bạn muốn giết.

0

Trong nhận xét, bạn nói rằng vấn đề là các hệ thống khác nhau (đặc biệt là MacOS và Linux) thực thi các vị trí trong các thư mục khác nhau.

Bạn có thể giải quyết vấn đề này bằng cách tạo thư mục có cùng đường dẫn đầy đủ trên cả hai hệ thống và tạo liên kết tượng trưng cho tệp thi hành.

Thử nghiệm trên Ubuntu, Solaris và Cygwin chỉ ra rằng tệp thực thi có tên trong một shebang có thể là một liên kết tượng trưng. (Tôi không có quyền truy cập vào một hệ thống hệ điều hành MacOS, vì vậy tôi không chắc chắn rằng nó sẽ làm việc ở đó.)

Ví dụ, trên hệ thống Ubuntu của tôi:

$ cat hello.bash 
#!/tmp/bin/bash 

echo Yes, it works 
$ ./hello.bash 
-bash: ./hello.bash: /tmp/bin/bash: bad interpreter: Permission denied 
$ mkdir /tmp/bin 
$ ln -s /bin/bash /tmp/bin/. 
$ ./hello.bash 
Yes, it works 
$ 

Thiết lập thư mục phổ biến trên tất cả các hệ thống liên quan được thừa nhận là bất tiện. (Tôi đã sử dụng /tmp cho ví dụ này; một vị trí khác có thể tốt hơn.)

Tôi không biết chắc cách này sẽ tương tác với killall, nhưng đáng để thử.

+0

Đó là điều, mặc dù; bạn không thể dựa vào ''/tmp/'' để tồn tại trên các lần khởi động lại, vì vậy kịch bản của bạn không thể dựa vào nó ở đó. Điều này là rất gần, mặc dù, bởi vì '' killall'' làm việc như mong đợi. –

+0

@DavidEllis: Đó là lý do tại sao tôi nói rằng một vị trí khác có thể tốt hơn - nhưng bạn có thể có một tập lệnh tạo và điền '/ tmp/bin' khi khởi động. Nó phụ thuộc vào mức độ kiểm soát bạn có trên hệ thống. –