Làm thế nào để tôi * đáng tin cậy * và * đơn giản * có được tên trình thông dịch shell hiện tại?


9

Tôi đang tìm kiếm một cách đơn giản và đáng tin cậy để lấy tên của trình bao hiện tại từ bên trong tập lệnh hoặc tệp có nguồn gốc ( không phải từ dòng lệnh). Tôi đã dự kiến ​​sẽ làm $(basename "$SHELL")nhưng nếu vỏ đăng nhập của tôi là zshvà tôi có mã sau đây trong some_script.sh

this_shell=$( basename "$SHELL" )
echo "The Shell is $this_shell"
echo "The Shell is $0"

và tôi chạy nó với bash some_script.sh, nó vẫn liệt kê zshthay vì bashmặc dù trình thông dịch được sử dụng /bin/bash. Tôi không chắc tại sao các nhà thiết kế shell lại chọn hiển thị shell mặc định thay vì shell hiện tại, nhưng đó dường như là những gì chúng tôi đang mắc kẹt.

Có một số câu hỏi hơi giống nhau khác ( ở đâyở đây ), nhưng câu trả lời của họ bị thiếu theo nhiều cách.

  1. Họ thường cho rằng người dùng đang cố gắng tìm ra loại vỏ tương tác nào họ đang sử dụng, nhưng điều đó thật điên rồ. Tôi biết shell nào tôi đang gõ lệnh vào - Tôi cần mã có thể chạy ở bất cứ đâu để có thể xác định đang sử dụng shell nào .
  2. Họ thường đưa ra một số điều khác nhau để thử - một lần nữa như thể bạn đang ở trên dòng lệnh và chỉ có thể mân mê cho đến khi bạn biết bạn đang sử dụng bash - nhưng tôi cần một điều đáng tin cậy trong mọi ngữ cảnh.
  3. Họ thường đưa ra những thứ hoàn toàn vô dụng từ các kịch bản, như thế echo $0. Điều đó thất bại như thể hiện trong kịch bản trên. (Có, nó hoạt động trong một dòng lệnh shell tương tác, nhưng tại sao bạn sẽ không biết bạn đang sử dụng shell nào?)
  4. Đôi khi, họ đưa ra các lệnh (trong các thử nghiệm giới hạn của tôi) bao gồm thông tin chính xác, như ps -p $$, nhưng thiếu các lệnh sed / awk tương thích đa nền tảng để chuyển nó qua chỉ để lấy tên shell và bỏ qua các thông tin khác đi kèm Cho đi xe.
  5. Chúng bao gồm những thứ sẽ chỉ hoạt động trên một vài vỏ, như $BASH_VERSION$ZSH_VERSION. Tôi muốn hỗ trợ như nhiều vỏ càng tốt, chẳng hạn như fish, csh, tcsh.

Làm thế nào để tôi phát hiện chính xácđáng tin cậy bất kỳ vỏ hiện tại ? Tôi đang tìm kiếm thứ gì đó sẽ hoạt động trên các nền tảng, trong các tập lệnh và cho càng nhiều shell càng tốt và hợp lý 1 .

CẬP NHẬT : Khi tôi đăng câu hỏi này, tôi đã dự đoán rằng có một số cơ sở được tích hợp sẵn để cung cấp cho chúng tôi thông tin này, dường như không phải là trường hợp. Vì dường như không thể tránh khỏi việc dựa vào thứ gì đó bên ngoài vỏ, tôi nên nói rõ hơn rằng tôi đang yêu cầu mộtgiải pháp đa nền tảng (mặc dù ngụ ý phản đối của tôi đối với các câu trả lời khác ở trên, có thể dễ bị bỏ lỡ nếu bạn không 'Đọc kỹ câu hỏi).

Cập nhật 2 Nếu bất cứ ai vẫn tin rằng đây là bản sao của câu hỏi chỉ dành cho Linux vì câu trả lời của Stéphane không chỉ dành cho Linux, đây là những khác biệt giữa những gì tôi yêu cầu và những gì anh ấy đã cung cấp. (Lưu ý rằng những gì anh ấy đã viết là khéo léo và tôi không gõ nó, nhưng nó không giải quyết được vấn đề của tôi.)

  1. Tôi đang tìm kiếm một cái gì đó đơn giảnđáng tin cậy , đó là
  2. có thể được thêm vào bất kỳ định nghĩa kịch bản hay chức năng (mà có thể được bắt nguồn bởi một .zshrchoặc .bash_profilehoặc bất cứ điều gì) sang cành khác.
  3. Bạn không thể sử dụng tập lệnh của anh ấy như một tiện ích bên ngoài chuyển tên trình thông dịch sang tập lệnh / hàm gọi, vì nó sẽ luôn được trình thông dịch mặc định giải thích và trả về đó. Điều này làm cho nó khó khăn hoặc không thể sử dụng cho mục đích của tôi. Nếu có thể, nó vẫn còn rất rất khó, và giải pháp để làm cho nó hoạt động không được đưa ra trong câu trả lời. Do đó, ông không trả lời câu hỏi của tôi, do đó nó không phải là một bản sao.

Nếu bạn muốn thấy một cái gì đó sẽ hoạt động, hãy xem ShellDetective trên GitHub . Điều đó có thể giúp dễ dàng nhận thấy sự khác biệt giữa những gì đã có trên SE và câu hỏi này đang tìm kiếm điều gì (và thực tế được viết để đáp ứng nhu cầu của câu hỏi này, không được đáp ứng ở bất kỳ nơi nào khác).


(PS nếu bạn không thể tin rằng có một trường hợp sử dụng cho việc này, hãy tưởng tượng các chức năng mà có được nguồn vào .zshrc, .bash_profilehoặc .profiletùy thuộc vào những gì máy chủ đang được sử dụng và những gì nó có vỏ sẵn. Họ đang có nguồn gốc do đó họ không có dòng công việc. Họ là hữu ích nhất nếu chúng có thể hoạt động trong bất kỳ vỏ nào, nhưng đôi khi chúng phải biết chúng nằm trong vỏ nào để biết cách cư xử.)


1 Tôi không quan tâm đến "vỏ" không phải là vỏ theo bất kỳ ý nghĩa truyền thống nào của từ này. Tôi không quan tâm đến một số vỏ mà một số người viết cho chính mình. Tôi chỉ quan tâm đến các shell thực sự xảy ra trong tự nhiên và ai đó có thể hiểu được phải sử dụng khi đăng nhập vào một số máy chủ mà họ không thể cài đặt bất kỳ shell nào họ muốn.


Để đọc cuộc thảo luận hoàn toàn ngoài chủ đề về việc Python có phải là vỏ hay không: xem tại đây
iconoclast

2
Trích dẫn câu trả lời của Gilles , cho câu hỏi Sự khác biệt chính xác giữa 'thiết bị đầu cuối', 'vỏ', 'tty' và 'bàn điều khiển' là gì?   - Một vỏ là giao diện chính mà người dùng nhìn thấy khi họ đăng nhập, mục đích chính của họ là bắt đầu các chương trình khác. IMO, thế là đủ để loại bỏ Perl và Python khỏi danh mục của shell shell.
G-Man nói 'Phục hồi Monica'

Liên quan mơ hồ: Kịch bản tương thích: Tiết kiệm $? để sử dụng sau . Một cách tiếp cận được đề xuất: thực hiện kết thúc về sự khác biệt trong các cú pháp của các shell khác nhau bằng cách nói sh -c "…", và sau đó thực hiện phần lớn công việc theo cú pháp shell POSIX.
G-Man nói 'Phục hồi Monica'

2
Tại sao bạn muốn có được điều đó ? Một số shell, có thể sử dụng làm shell đăng nhập, có cú pháp rất không tương thích (một số shell là ngôn ngữ giống Lisp!), Vì vậy tôi không hiểu tại sao bạn muốn làm điều đó. Nếu bạn đang mã hóa các tệp có thể tìm được, bạn sẽ cần mã một số biến thể và báo cho người dùng của bạn chọn một biến thích hợp.
Basile Starynkevitch

1
Câu hỏi thứ hai của tôi là một ví dụ về việc ngụy trang một cái vỏ. Bạn sẽ làm gì nếu một tập lệnh được thực thi bởi một trình thông dịch có thể thực thi được có tên không khớp với ngôn ngữ mà trình thông dịch phiên dịch? Nếu tôi lấy một bản sao của csh, đổi tên nó thành fishvà sử dụng nó để chạy tập lệnh của bạn, bạn có muốn fishhay cshkhông?
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


5

Tôi đã có kết quả tuyệt vời với sự kết hợp này:

ps -p $$ | awk '$1 != "PID" {print $(NF)}'

Trên Tru64 (OSF / 1), đầu ra có dấu ngoặc đơn quanh vỏ. Tack vào tr -d '()'để loại bỏ chúng.

ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()'  

Xuất hiện để hoạt động trên tất cả các vỏ, trên Solaris 10 và RHEL 5.7 / 6.4. Không kiểm tra các bản phát hành khác như Debian, Ubuntu hoặc Mint, nhưng tôi nghĩ tất cả chúng đều hoạt động như nhau; Tôi cũng không biết FreeBSD có hoạt động không.

nhập mô tả hình ảnh ở đây


3
Không hoạt động trong tập lệnh (thay vào đó sẽ trả lại tên của tập lệnh)
Qw3ry

2

Vì vậy, tôi vẫn đang cố gắng để làm rõ điều này, nhưng tôi nghĩ rằng tôi có một ý tưởng sẽ hoạt động. Như bạn đã nhận thấy, những gì bạn đang cố gắng làm là nếu không thể, cực kỳ khó thực hiện trong tất cả các shell (mỗi biến thể bạn thêm vào polyglot làm tăng độ phức tạp ở tốc độ lớn hơn tốc độ tuyến tính). bạn có thể làm điều đó nếu bạn chia thành Bourne (sh, ash, dash, bash, ksh, pdksh, zsh, v.v.) và vỏ kiểu c (csh, tcsh, fish, v.v.) nhưng sự khác biệt giữa cshcác biến thể thể hiện sự thú vị khác nhau những thách thức

Vì vậy, hãy gian lận và viết thói quen phát hiện của chúng tôi bằng một ngôn ngữ đã biết (bash với một dòng shebang, C, perl, python, bất cứ điều gì) và xác định tên của tệp thực thi của cha mẹ. sau đó chúng ta có thể sử dụng hai thủ thuật để lấy lại thông tin cho cha mẹ, đó là giá trị trả về (s) và một từ duy nhất được viết theo tiêu chuẩn. Trên các vỏ đạn giảm dần, chúng ta sẽ trả về 0 và viết tên của vỏ vì tất cả chúng đều có khả năng xử lý backtick tốt. trên họ C shell, chúng ta có thể bắt đầu tăng giá trị trả về cho mỗi bộ không tương thích mà chúng ta cần xử lý. Giá trị trả về hơn hai trăm sẽ cho thấy lỗi bắt đầu với mọi thứ có vẻ ổn, nhưng tôi không thể biết cha mẹ mình là ai trong hai trăm.

Chương trình bên trong có vẻ dễ dàng trên linux ( /proc/ppid/exelà một nửa trận chiến). Tôi khá chắc chắn rằng điều này có thể được thực hiện trên bsd với các tùy chọn ps, nhưng hiện tại tôi không có hệ thống bsd đang chạy để kiểm tra và mac của tôi cần một ổ cứng mới (dù sao chỉ là 10,4). Mặc dù nó làm giảm đáng kể các vấn đề cú pháp shell, nhưng nó lại đưa ra một loạt các vấn đề tương thích khác. Tôi vẫn nghĩ rằng nó có khả năng.


Đây là cái nhìn sâu sắc mà cuối cùng tôi đã giải quyết trên tinh thần rằng bạn cần một chương trình bên ngoài để thực hiện công việc, mặc dù tôi đã nhận được nó khi đọc bình luận của G-Man sh -c "...", mà anh ấy đã lấy từ một câu hỏi khác . (Xin lỗi: Ban đầu tôi đã bỏ qua câu trả lời của bạn vì tôi không thấy bất kỳ mã nào trong đó và chỉ quay lại và đọc nó sau.)
iconoclast

Có một nghịch lý trong câu hỏi của @ iconoclast. Anh ta muốn gọi nó từ một tập lệnh (lưu ý rằng một số shell không hỗ trợ các hàm) có thể có nguồn gốc từ bất kỳ shell nào, do đó, script có lẽ đã là polyglot, vì vậy phải có một số hỗ trợ để tìm hiểu những gì đang diễn giải nó. Theo kinh nghiệm của tôi, việc viết mã polyglot ( my_intepreter là một ví dụ cực đoan, nhưng cũng thấy ở đó chỉ có một vỏ là cực kỳ khó khăn và thường không đáng để nỗ lực.
Stéphane Chazelas 17/8/2015

@ StéphaneChazelas: Tôi nghĩ bạn nói đúng rằng nó thường không đáng để nỗ lực (chắc chắn không, và có thể luôn luôn không) nhưng tôi chưa sẵn sàng từ bỏ ... Tôi nghĩ vấn đề thực sự bị phá vỡ thành 2 vấn đề: tên shell và thứ 2 sử dụng nó. Cả hai (ít nhất là thực tế) yêu cầu một số trợ giúp bên ngoài để tránh những hạn chế của đạn pháo mà bạn có thể đang sử dụng. Tôi đã giải quyết vấn đề đầu tiên với một tiện ích nhỏ được viết bằng Crystal và khi tôi tìm thấy thời gian tôi cũng muốn giải quyết vấn đề thứ hai. Btw, giải pháp của bạn là tuyệt vời, nhưng rõ ràng là rất phức tạp.
iconoclast

@iconoclas, giả sử bạn có thể truy xuất tên shell một cách đáng tin cậy (lưu ý rằng thay thế lệnh và cú pháp gán biến khác nhau giữa các shell), bạn không thể thực hiện những việc như case $shell in sh)...(cú pháp chỉ Bourne) hoặc switch ($shell)(chỉ csh). test "$shell" = "sh" && do-somethinghoạt động trong csh/ sh/ rc/ es, nhưng không fish...
Stéphane Chazelas

vâng, tôi đã chạy vào đây rồi Điều tồi tệ nhất là cá thậm chí sẽ không tải một tập lệnh có cú pháp mà nó không thích, vì vậy bạn không thể gửi STDERR tới /dev/null. Nhưng tôi có một giải pháp, mà tôi sẽ đăng khi tôi đã làm xong.
iconoclast

1

hãy thử những thứ như thế này

shell_bin=$(ps h -p $$ -o args='' | cut -f1 -d' ')
echo $shell_bin

Điều này thất bại trong cshtcsh.
iconoclast

với một sửa đổi nhỏ, nó sẽ hoạt động fish, nhưng sau đó chỉ trong fish, không may : ps h -p %self -o args='' | cut -f1 -d' '. Điều này tạo ra một vấn đề về gà và trứng ... bạn phải biết rằng bạn đang fishthực thi fishphiên bản mã ...
iconoclast

@ iconoclast- set shell_bin=`ps -p $$ -o args='' | cut -f1 -d' '`- sẽ hoạt động với CSH nhưng không hoạt động với những người khác
DarkHeart

@DarkHeart: thực sự không thành công, vì việc gán biến không phải là vấn đề trên csh/ tcsh, đó là phần này : ps -p $$ -o args='' | cut -f1 -d' ', trả -shvề csh-cshbật tcsh.
iconoclast

1

Tôi tin rằng bạn không thể luôn luôn có được tên của shell hiện tại và tôi nghĩ bạn nên nhận thức được những hạn chế của những gì có thể.

Trên các bản phân phối Linux, hầu hết người dùng sẽ có bash vỏ đăng nhập và tương tác của họ (vì đây bashlà vỏ mặc định trên hầu hết các bản phân phối). Một số người dùng sẽ đặt vỏ của họ để zsh, csh(và các biến thể) hoặc fish.

(Như ý kiến và câu trả lời khác giải thích, tìm cách đáng tin cậy vỏ ra khỏi bash, zsh, tcsh, fishđã được thử thách)

Nhưng một số người dùng có thể thiết lập lạ vỏ đăng nhập của họ đến một cái gì đó hoàn toàn khác nhau - một thông dịch Lisp, scsh, es, một số ngôn ngữ kịch bản à la Python, hoặc Ocaml, hoặc Perl, vv ...- và nó là sự tự do của họ để làm như vậy. Có lẽ, một số người đang mã hóa vỏ của chính họ và sử dụng nó một cách tương tác. Ngay cả khi bạn tìm thấy cái vỏ kỳ lạ của chúng, bạn sẽ không thể làm bất cứ điều gì hữu ích về nó (do đó tôi tin rằng bạn không nên cố lấy tên của cái vỏ).

Vì vậy, tôi đoán rằng bạn đang mã hóa một số tệp có thể tìm được (có thể tạo một tệp) để định cấu hình một số phần mềm. Vì vậy, chỉ cần giải thích những gì bạn đang làm và mã cho trường hợp phổ biến của bash(và có lẽ zsh& tcsh) ....


-2

sử dụng dưới đây từ chối trách nhiệm rủi ro

tmp=`head -n 1 $0`
SHELL_BIN=`readlink -f ${tmp#*!}`
SHELL=${SHELL_BIN##*/}

nó cho tôi một dòng trống ... nhưng nếu tôi hiểu những gì bạn đang cố gắng làm thì nó rất thông minh
iconoclast

Nó không nên cung cấp cho bạn một dòng trống, trừ khi -ftùy chọn trong readlink, một số triển khai không có công tắc đó
gwillie

Tôi đang dùng OS X và trang man liệt kê -fdưới dạng tùy chọn khả dụng, lấy tham số formatlàm đối số.
iconoclast

Điều đó có thể hoạt động trong một số trường hợp đặc biệt nếu $ 0 là tập lệnh (bắt đầu bằng #! / ...) nhưng đó không phải là thiết lập thông thường. Ở đây, hầu hết $ 0 trỏ đến tệp ELF.

readlinkkhông làm việc trên puredarwin vm của tôi vì vậy tôi không biết những gì đang xảy ra ở đó.
gwillie
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.