xác định shell trong script trong thời gian chạy


22

Theo hiểu biết của tôi, để xác định vỏ hiện tại chúng ta sử dụng echo $0trong vỏ. Thay vào đó tôi muốn tập lệnh của tôi kiểm tra xem nó đang chạy trong shell nào. Vì vậy, tôi đã cố gắng in $0trong tập lệnh và nó trả về tên của tập lệnh như bình thường. Vì vậy, câu hỏi của tôi là làm thế nào tôi có thể tìm thấy shell nào là tập lệnh của tôi đang chạy trong thời gian chạy?


bạn đang sử dụng ngôn ngữ kịch bản nào? Ngoài ra, trong trường hợp xấu hơn, bạn luôn có thể trình bày một lệnh hệ thống để nhận kết quả "echo $ 0" bên trong tập lệnh.
BriGuy

echo $0không phải là một tùy chọn ở đây, vì tập lệnh sẽ chạy trên nhiều máy khác nhau, trong đó điều đầu tiên tôi cần kiểm tra là trình bao.
g4ur4v

Vậy ngôn ngữ kịch bản là gì?
BriGuy

@BriGuy: Đó là một tập lệnh shell unix.
g4ur4v

3
Vâng, nếu bạn thêm #! /bin/sh -ở đầu, nó sẽ chạy vào sh. Bạn có nghĩa là biến thể của shnó là gì?
Stéphane Chazelas

Câu trả lời:


28

Trên linux bạn có thể sử dụng /proc/PID/exe.

Thí dụ:

# readlink /proc/$$/exe
/bin/zsh

3
Đó là một chút quá cụ thể đối với tôi (ví dụ trên Debian nó in zsh4 hoặc ksh93). /bin/sed -r -e 's/\x0.*//' /proc/$$/cmdlinethay vào đó cho zsh hoặc ksh. (Đó sẽ là $ 0 nếu shell không sửa lỗi một cách kỳ diệu để đặt tên tập lệnh thay thế).
frostschutz

@frostschutz Yours là câu trả lời tốt nhất, hãy chạy cho +500!
Teresa e Junior

5
Điều này phải chịu đựng căn bệnh Linux khủng khiếp . /proclà xấu xí và không thể quan sát như nó được.
Jens

7
@Jens đó là lý do tại sao tôi chỉ định điều này chỉ áp dụng cho Linux. /prockhông phải là "xấu xí". /procthường là một giải pháp rất thanh lịch. Không thể có được, nhưng bởi vì một cái gì đó không thể quan sát được sẽ không làm cho nó xấu đi.
Patrick

3
@Patrick Tôi coi là /procxấu vì các tệp trong đó có thể đến và đi theo ý thích của các nhà phát triển và nội dung của các tệp có xu hướng thay đổi mà không cần thông báo, gây ra nỗi đau vô tận do bitrot và di chuyển định dạng tệp mục tiêu.
Jens

48

Có thể không phải là những gì bạn đang yêu cầu, nhưng điều này sẽ hoạt động ở một mức độ nào đó để xác định trình thông dịch hiện đang phiên dịch nó cho một số người như Thompson (osh), Bourne, Bourne-again (bash), Korn (ksh88, ksh93, pdksh, mksh ), zsh, Ordinary tuân thủ chính sách (posh), Yet Another (yash), rc, akanga, es shells, wish, tclsh, mong đợi, perl, python, ruby, php, JavaScript (nodejs, SpiderMonkey shell và JSPL ít nhất) , MS / Wine cmd.exe, lệnh.com (MSDOS, FreeDOS ...).

'echo' +"'[{<?php echo chr(13)?>php <?php echo PHP_VERSION.chr(10);exit;?>}\
@GOTO DOS [exit[set 1 [[set 2 package] names];set 3 Tcl\ [info patchlevel];\
if {[lsearch -exact $1 Expect]>=0} {puts expect\ [$2 require Expect]\ ($3)} \
elseif {[lsearch -exact $1 Tk]>=0} {puts wish\ ($3,\ Tk\ [$2 require Tk])} \
else {puts $3}]]]' >/dev/null ' {\">/dev/null \
">"/dev/null" +"\'";q="#{",1//2,"}";a=+1;q='''=.q,';q=%{\"
'echo' /*>/dev/null
echo ">/dev/null;status=0;@ {status=1};*=(" '$' ");~ $status 1&&{e='"\
"';eval catch $2 ^'&version {eval ''echo <='^ $2 ^'&version''}';exit};e='"\
"';if (eval '{let ''a^~a''} >[2] /dev/null'){e='"\
"';exec echo akanga};eval exec echo rc $2 ^ version;\" > /dev/null
: #;echo possibly pre-Bourne UNIX V1-6 shell;exit
if (! $?version) set version=csh;exec echo $version
:DOS
@CLS
@IF NOT "%DOSEMU_VERSION%"=="" ECHO DOSEMU %DOSEMU_VERSION%
@ECHO %OS% %COMSPEC%
@VER
@GOTO FIN
", unless eval 'printf "perl %vd\n",$^V;exit;'> "/dev/null";eval ': "\'';
=S"';f=false e=exec\ echo n=/dev/null v=SH_VERSION;`(eval "f() { echo :
};f")2>$n` $f||$e Bourne-like shell without function
case `(: ${_z_?1}) 2>&1` in 1) $e ash/BSD sh;;esac;t(){
eval "\${$1$v+:} $f &&exec echo ${2}sh \$$1$v";};t BA ba;t Z z;t PO po;t YA ya
case `(typeset -Z2 b=0;$e $b)2>$n` in 00) (eval ':${.}')2>$n&&eval '
$e ksh93 ${.sh.version}';t K pdk;$e ksh88;;esac;case `(eval '$e ${f#*s}$($e 1
)$((1+1))')2>$n` in e12)$e POSIX shell;;esac;$e Bourne-like shell;: }
print "ruby ",RUBY_VERSION,"\n";exit;' ''';import sys
print("python "+sys.version);z='''*/;
s="";j="JavaScript";if(typeof process=="object"){p=console.log;p(process.title
,process.version)}else{p=print;p((f="function")==(t=typeof version)?"string"==
typeof(v=version())?v:(typeof build!=f?"":s= "SpiderMonkey ")+j+" "+v:(t==
"undefined"?j+"?":version)+"\n");if(s)build()}/*
:FIN } *///'''

Tôi đã đăng phiên bản ban đầu của tập lệnh which_interpreter vào khoảng năm 2004 trên usenet. Sven Mascheck có một tập lệnh (có thể hữu ích hơn cho bạn) được gọi là whatshell tập trung vào việc xác định các shell giống như Bourne. Bạn cũng có thể tìm thấy một phiên bản hợp nhất của hai tập lệnh của chúng tôi ở đó .


3
Điều này không thể xác định Python 3, chỉ Python 2. Để sửa lỗi đó, hãy thay đổi printthành một hàm.
Chris Down

39
Đây là thời điểm WTF lớn nhất trong năm cho đến nay. +1 để lấy tính di động trong quá khứ.
l0b0

1
Sẽ thật tuyệt nếu nó nhận ra vỏ cá.
Konrad Borowski

2
@xfix, tôi nhớ đã thử ngay cả trước khi thêm php và javascript nhưng không thể tìm thấy giải pháp sau đó. Độ phức tạp tăng theo cấp số nhân với số lượng ngôn ngữ cần hỗ trợ (vì mọi thứ bạn thêm phải hợp lệ (hoặc ít nhất là có tác dụng phụ không đáng chú ý) trong tất cả các ngôn ngữ được hỗ trợ) nên giờ đây thậm chí còn khó khăn hơn. Tôi không nói là không thể nhưng điều đó có thể có nghĩa là bỏ hỗ trợ cho một số ngôn ngữ khác.
Stéphane Chazelas

4
@iconoclast, vì vậy nó xác định chính xác bash 3.2.53(1)-releaselà thông dịch viên phiên dịch nó.
Stéphane Chazelas

12

Đây là những gì tôi sử dụng trong .profile của mình để kiểm tra các shell khác nhau trên các hệ thống tôi làm việc. Nó không tạo ra sự khác biệt tốt giữa ksh88 và ksh93, nhưng nó chưa bao giờ làm tôi thất bại.

Lưu ý rằng nó không yêu cầu một ngã ba hoặc đường ống.

# Determine what (Bourne compatible) shell we are running under. Put the result
# in $PROFILE_SHELL (not $SHELL) so further code can depend on the shell type.

if test -n "$ZSH_VERSION"; then
  PROFILE_SHELL=zsh
elif test -n "$BASH_VERSION"; then
  PROFILE_SHELL=bash
elif test -n "$KSH_VERSION"; then
  PROFILE_SHELL=ksh
elif test -n "$FCEDIT"; then
  PROFILE_SHELL=ksh
elif test -n "$PS3"; then
  PROFILE_SHELL=unknown
else
  PROFILE_SHELL=sh
fi

1
Lưu ý rằng chỉ có các phiên bản gần đây của ksh93$KSH_VERSION. Biến đó xuất phát từ pdkshvà không bao giờ được chuyển sang AT & T ksh88.
Stéphane Chazelas

Phải, đó là lý do tại sao tôi có bài kiểm tra thứ hai cho FCEDIT.
Jens

1
Đúng. Lưu ý rằng posh(pdksh với hầu hết các tính năng không phải POSIX đã bị xóa nên bạn có thể muốn gọi nó là "sh") không có FCEDIT cũng như KSH_VERSION nhưng có PS3 (có thể không lâu), mặc dù không thể có nó làm vỏ đăng nhập . Cũng lưu ý rằng mã ở trên sẽ không phản ánh việc có bashhoặc zshđang ở shchế độ mô phỏng hay không , đây có thể là vấn đề nếu bạn đang sử dụng $PROFILE_SHELLđể quyết định có bật tính năng này hay không. Xem thêm whatshell của Sven Mascheck để biết thêm bạn có thể (hoặc có thể không) muốn kiểm tra.
Stéphane Chazelas

6

Bạn có thể thử

ps -o args= -p "$$"

Nó sẽ cho bạn tên của lệnh được liên kết với pid của script.


Không hoạt động khi sử dụng shebang như tôi có thể nói. spunge.us/QeHD
Chris Down

Xin lỗi, @ChrisDown, Flup. Xấu của tôi, tôi đã sai dịch cmdđến commkhi POSIXifying câu trả lời.
Stéphane Chazelas

1

Nếu có lsoflệnh có sẵn trên hệ thống của bạn, bạn có thể nhận được đường dẫn đầy đủ của shell cha thực thi bằng cách đưa PID cha qua psvà phân tích cú pháp của lsof -p $ppid(xem Cách xác định shell hiện tại tôi đang làm việc? ).

#!/bin/sh
ppid="`ps -p "$$" -o ppid=`"
lsof -nP -p "$ppid" | awk 'NR==3 {print $NF; exit}'

Trên hệ thống của tôi, điều này trả về /, nếu tôi sử dụng, NR==4tôi nhận được đường dẫn đến shell shell.
Thor

Lưu ý rằng POSIX shs có $PPIDbiến. Trên Linux, bạn có thể sử dụng readlink -f "/proc/$PPID/exe".
Stéphane Chazelas

1

Ngoài vùng đất Linux hoặc thiếu quyền truy cập vào hệ thống tập tin / Proc hoặc tương đương, bạn có thể sử dụng pstree:

Giả sử bạn có pid của

Trên máy Mac:

./test.sh 
16012
-+= 00001 root /sbin/launchd
 \-+= 00245 wingwong /sbin/launchd
   \-+= 04670 wingwong /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal -psn_0_2052597
     \-+= 11816 root login -pf wingwong
       \-+= 11817 wingwong -bash
         \-+= 16012 wingwong ksh ./test.sh
           \-+- 16013 wingwong pstree -p 16012

Trên hộp Linux:

./test.sh 
14981
bash(14981)---pstree(14982)

Định dạng và kiểu của đầu ra từ pstree khác nhau, tùy thuộc vào môi trường của bạn, nhưng bạn có thể thực thi đầu ra ASCII và sau đó sed / tr / awk / etc. lọc đầu ra để lấy shell đang chạy script.

Vì vậy, một phiên bản đầu ra đã được dọn sạch (hoạt động cho hệ điều hành Mac hoặc Linux):

#!/usr/bin/env sh
pstree  -p $$  | tr ' ()' '\012\012\012' | grep -i "sh$" | grep -v "$0" | tail -1

Về năng suất chạy:

./test.sh 
sh

Và khi chạy với lớp vỏ khác:

#!/usr/bin/env ksh
pstree  -p $$  | tr ' ()' '\012\012\012' | grep -i "sh$" | grep -v "$0" | tail -1

Sản lượng:

./test.sh 
ksh

Không có hệ thống tập tin gốc hoặc đặc biệt cần thiết. Lưu ý, bộ lọc của tôi giả định rằng tên nhị phân shell kết thúc bằng sh và không có mục trung gian nào kết thúc bằng sh. Cũng giả sử rằng bạn đã không đặt tên cho tập lệnh của mình là "sh" hoặc một số mẫu grep không may sẽ xóa thông tin. :) Sẽ yêu cầu một số tùy chỉnh cho môi trường của riêng bạn để đảm bảo mức độ bảo vệ cao hơn.


-2

Bạn có thể sử dụng lệnh:

$ echo $SHELL

để tìm ra shell từ trong script.


18
Số $SHELLlà vỏ của sự lựa chọn của người dùng. Khởi tạo từ vỏ đăng nhập của người dùng. Không có gì để làm với vỏ hiện đang chạy.
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.