Làm thế nào tôi có thể xác định shell hiện tại tôi đang làm việc?
Chỉ đầu ra của ps
lệnh sẽ là đủ?
Làm thế nào điều này có thể được thực hiện trong các hương vị khác nhau của Unix?
Làm thế nào tôi có thể xác định shell hiện tại tôi đang làm việc?
Chỉ đầu ra của ps
lệnh sẽ là đủ?
Làm thế nào điều này có thể được thực hiện trong các hương vị khác nhau của Unix?
Câu trả lời:
Có ba cách tiếp cận để tìm tên của shell hiện tại:
Xin lưu ý rằng cả ba cách tiếp cận đều có thể bị đánh lừa nếu khả năng thực thi của shell là /bin/sh
, nhưng nó thực sự được đổi tên bash
, chẳng hạn (thường xảy ra).
Do đó, câu hỏi thứ hai của bạn về việc liệu ps
đầu ra sẽ làm được trả lời với " không phải luôn luôn ".
echo $0
- sẽ in tên chương trình ... mà trong trường hợp shell là shell thực tế.
ps -ef | grep $$ | grep -v grep
- cái này sẽ tìm ID tiến trình hiện tại trong danh sách các tiến trình đang chạy. Vì quy trình hiện tại là vỏ, nó sẽ được bao gồm.
Điều này không đáng tin cậy 100%, vì bạn có thể có các quy trình khác có ps
danh sách bao gồm cùng số với ID quy trình của shell, đặc biệt nếu ID đó là một số nhỏ (ví dụ: nếu PID của shell là "5", bạn có thể tìm thấy các quy trình được gọi "Java5" hoặc "perl5" trong cùng một grep
đầu ra!). Đây là vấn đề thứ hai với cách tiếp cận "ps", trên hết là không thể dựa vào tên shell.
echo $SHELL
- Đường dẫn đến shell hiện tại được lưu trữ dưới dạng SHELL
biến cho bất kỳ shell nào. Thông báo trước cho điều này là nếu bạn khởi chạy một lớp vỏ rõ ràng dưới dạng một quy trình con (ví dụ: đó không phải là vỏ đăng nhập của bạn), bạn sẽ nhận được giá trị của vỏ đăng nhập thay thế. Nếu đó là một khả năng, sử dụng ps
hoặc $0
cách tiếp cận.
Tuy nhiên, nếu thực thi không khớp với vỏ thực tế của bạn (ví dụ như /bin/sh
thực sự là bash hoặc ksh), bạn cần heuristic. Dưới đây là một số biến môi trường cụ thể cho các loại vỏ khác nhau:
$version
được đặt trên tcsh
$BASH
được đặt trên bash
$shell
(chữ thường) được đặt thành tên shell thực tế trong csh hoặc tcsh
$ZSH_NAME
được đặt trên zsh
ksh có $PS3
và $PS4
thiết lập, trong khi shell Bourne bình thường ( sh
) chỉ có $PS1
và $PS2
thiết lập. Điều này thường có vẻ như khó khăn nhất để phân biệt - các chỉ chênh lệch trong toàn bộ tập hợp các biến môi trường giữa sh
và ksh
chúng tôi đã cài đặt trên Solaris boxen là $ERRNO
, $FCEDIT
, $LINENO
, $PPID
, $PS3
, $PS4
,$RANDOM
, $SECONDS
, và $TMOUT
.
echo ${.sh.version}
trả về "Thay thế xấu". Xem giải pháp của tôi ở trên
ps -ef | grep …
... Đây không phải là 100% đáng tin cậy như ...” Sử dụng một biểu thức chính quy đơn giản thông qua egrep
hoặc grep -e
có thể dễ dàng mang lại độ tin cậy lên đến cho tất cả-intents-and-mục đích 100%: ps -ef | egrep "^\s*\d+\s+$$\s+"
. Các ^
làm cho chắc chắn chúng tôi đang bắt đầu từ đầu dòng, các \d+
ngốn lên UID thì $$
phù hợp với PID, và \s*
và \s+
tài khoản cho & đảm bảo khoảng trắng giữa các bộ phận khác.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
nên hoạt động ở bất cứ nơi nào mà các giải pháp liên quan ps -ef
và grep
thực hiện (trên bất kỳ biến thể Unix nào hỗ trợ các tùy chọn POSIX chops
) và sẽ không phải chịu các lỗi tích cực được đưa ra bằng cách grepping cho một chuỗi các chữ số có thể xuất hiện ở nơi khác.
ps
có thể không hiểu -p
nên bạn có thể cần sử dụng /bin/ps -p $$
.
$$
ngoại trừ việc fish
bạn sẽ phải sử dụng ps -p %self
.
/bin/ps
. ps
có thể dễ dàng (thực sự là nó khá bình thường ngày nay) được cài đặt trong /usr/bin
. $(which ps) -p $$
là một cách tốt hơn. Tất nhiên, điều này sẽ không hoạt động trong cá, và có thể một số vỏ khác. Tôi nghĩ rằng nó là (which ps) -p %self
trong cá.
readlink /proc/$$/exe
sh
được mô phỏng theo bash
, ps -p cung cấp cho bạn /usr/bin/bash
ngay cả khi bạn chạy nó nhưsh
Thử
ps -p $$ -oargs=
hoặc là
ps -p $$ -ocomm=
ps -o fname --no-headers $$
.
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
. (Dòng tiếp theo trong tập lệnh của tôi có tương đương với csh.)
-q
thay vì -p
:SHELL=$(ps -ocomm= -q $$)
Nếu bạn chỉ muốn đảm bảo người dùng đang gọi một tập lệnh với Bash:
if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi
#!/bin/bash
: if [ ! -n "$BASH" ] ;then exec bash $0; fi
. Với dòng này, tập lệnh được chạy bằng bash ngay cả khi bắt đầu sử dụng ksh hoặc sh. Ca sử dụng của tôi không cần đối số dòng lệnh, nhưng chúng có thể được thêm vào sau $0
nếu cần.
Bạn co thể thử:
ps | grep `echo $$` | awk '{ print $4 }'
Hoặc là:
echo $SHELL
/pattern/ { action }
sẽ làm gì?
$SHELL
biến môi trường chứa shell, được cấu hình làm mặc định cho người dùng hiện tại. Nó không phản ánh một vỏ, hiện đang chạy. Ngoài ra, nó tốt hơn để sử dụng ps -p $$
hơn grepping $$ vì dương tính giả.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
không cần phải luôn luôn hiển thị vỏ hiện tại. Nó chỉ phản ánh shell mặc định được gọi.
Để kiểm tra ở trên, giả sử bash
là shell mặc định, hãy thử echo $SHELL
, và sau đó trong cùng một thiết bị đầu cuối, hãy vào một số shell khác ( ví dụ KornShell (ksh)) và thử$SHELL
. Bạn sẽ thấy kết quả là bash trong cả hai trường hợp.
Để có được tên của shell hiện tại, sử dụng cat /proc/$$/cmdline
. Và đường dẫn đến shell thực thi bởi readlink /proc/$$/exe
.
/proc
.
Tôi có một mẹo đơn giản để tìm vỏ hiện tại. Chỉ cần gõ một chuỗi ngẫu nhiên (không phải là một lệnh). Nó sẽ thất bại và trả về lỗi "không tìm thấy", nhưng ở đầu dòng, nó sẽ cho biết đó là shell nào:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
echo 'aaaa' > script; chmod +x script; ./script
cho./script.sh: 1: aaaa: not found
Sau đây sẽ luôn cung cấp shell thực tế được sử dụng - nó lấy tên của thực tế thực tế chứ không phải tên shell (tức là ksh93
thay vì ksh
, v.v.). Đối với /bin/sh
, nó sẽ hiển thị vỏ thực tế được sử dụng, tức là dash
.
ls -l /proc/$$/exe | sed 's%.*/%%'
Tôi biết rằng có nhiều người nói rằng ls
đầu ra không bao giờ được xử lý, nhưng xác suất bạn sẽ có một vỏ bạn đang sử dụng được đặt tên bằng các ký tự đặc biệt hoặc được đặt trong một thư mục có tên các ký tự đặc biệt là gì? Nếu đây vẫn là trường hợp, có rất nhiều ví dụ khác về việc làm nó khác đi.
Như Toby Speight đã chỉ ra , đây sẽ là một cách đúng đắn và gọn gàng hơn để đạt được điều tương tự:
basename $(readlink /proc/$$/exe)
/proc
. Không phải tất cả thế giới một hộp Linux.
basename $(readlink /proc/$$/exe)
để ls
+ sed
+ echo
.
ash -> /bin/busybox
, cái này sẽ cho / bin / busybox.
Tôi đã thử nhiều cách tiếp cận khác nhau và cách tốt nhất đối với tôi là:
ps -p $$
Nó cũng hoạt động theo Cygwin và không thể tạo ra dương tính giả khi grepping PID. Với một số dọn dẹp, nó chỉ xuất ra một tên thực thi (dưới Cygwin với đường dẫn):
ps -p $$ | tail -1 | awk '{print $NF}'
Bạn có thể tạo một chức năng để bạn không phải ghi nhớ nó:
# Print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
... và sau đó chỉ cần thực thi shell
.
Nó đã được thử nghiệm theo Debian và Cygwin.
ps
, tail
và gawk
, cmd không định nghĩa $$
là PID nên nó chắc chắn không thể hoạt động dưới cmd đơn giản.
ps -p$$ -o comm=
? POSIX nói rằng việc chỉ định tất cả các tiêu đề trống sẽ loại bỏ hoàn toàn tiêu đề. Chúng tôi vẫn thất bại (giống như tất cả các ps
câu trả lời) khi chúng tôi có nguồn gốc từ một tập lệnh được thực thi trực tiếp (ví dụ #!/bin/sh
).
Biến thể của tôi về in quy trình cha mẹ:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
Đừng chạy các ứng dụng không cần thiết khi AWK có thể làm điều đó cho bạn.
awk
, khi nào ps -p "$$" -o 'comm='
có thể làm điều đó cho bạn?
Có nhiều cách để tìm ra vỏ và phiên bản tương ứng của nó. Dưới đây là một số ít làm việc cho tôi.
Nói thẳng
Cách tiếp cận táo bạo
$> ******* (Nhập một tập hợp các ký tự ngẫu nhiên và trong đầu ra, bạn sẽ nhận được tên shell. Trong trường hợp của tôi -bash: chương2-a-sample-isomorphic-app: không tìm thấy lệnh )
Với điều kiện là bạn /bin/sh
hỗ trợ tiêu chuẩn POSIX và hệ thống của bạn đã lsof
cài đặt lệnh - một giải pháp thay thế khả dĩ lsof
trong trường hợp này là pid2path
- bạn cũng có thể sử dụng (hoặc điều chỉnh) tập lệnh sau in các đường dẫn đầy đủ:
#!/bin/sh
# cat /usr/local/bin/cursh
set -eu
pid="$$"
set -- sh bash zsh ksh ash dash csh tcsh pdksh mksh fish psh rc scsh bournesh wish Wish login
unset echo env sed ps lsof awk getconf
# getconf _POSIX_VERSION # reliable test for availability of POSIX system?
PATH="`PATH=/usr/bin:/bin:/usr/sbin:/sbin getconf PATH`"
[ $? -ne 0 ] && { echo "'getconf PATH' failed"; exit 1; }
export PATH
cmd="lsof"
env -i PATH="${PATH}" type "$cmd" 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }
awkstr="`echo "$@" | sed 's/\([^ ]\{1,\}\)/|\/\1/g; s/ /$/g' | sed 's/^|//; s/$/$/'`"
ppid="`env -i PATH="${PATH}" ps -p $pid -o ppid=`"
[ "${ppid}"X = ""X ] && { echo "no ppid found"; exit 1; }
lsofstr="`lsof -p $ppid`" ||
{ printf "%s\n" "lsof failed" "try: sudo lsof -p \`ps -p \$\$ -o ppid=\`"; exit 1; }
printf "%s\n" "${lsofstr}" |
LC_ALL=C awk -v var="${awkstr}" '$NF ~ var {print $NF}'
-i
tùy chọn (-> bỏ qua môi trường) env
trong dòng nơi bạn kiểm tra lsof
có sẵn. nó thất bại với: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
Nếu bạn chỉ muốn kiểm tra xem bạn có đang chạy (một phiên bản cụ thể của) Bash hay không, cách tốt nhất để làm điều đó là sử dụng $BASH_VERSINFO
biến mảng. Là một biến mảng (chỉ đọc), nó không thể được đặt trong môi trường, vì vậy bạn có thể chắc chắn rằng nó đang đến (nếu có) từ shell hiện tại.
Tuy nhiên, vì Bash có một hành vi khác khi được gọi là sh
, bạn cũng cần kiểm tra $BASH
biến môi trường kết thúc bằng /bash
.
Trong một kịch bản tôi đã viết sử dụng tên hàm với -
(không phải dấu gạch dưới) và phụ thuộc vào mảng kết hợp (được thêm vào Bash 4), tôi có kiểm tra độ tỉnh táo sau (với thông báo lỗi người dùng hữu ích):
case `eval 'echo $BASH@${BASH_VERSINFO[0]}' 2>/dev/null` in
*/bash@[456789])
# Claims bash version 4+, check for func-names and associative arrays
if ! eval "declare -A _ARRAY && func-name() { :; }" 2>/dev/null; then
echo >&2 "bash $BASH_VERSION is not supported (not really bash?)"
exit 1
fi
;;
*/bash@[123])
echo >&2 "bash $BASH_VERSION is not supported (version 4+ required)"
exit 1
;;
*)
echo >&2 "This script requires BASH (version 4+) - not regular sh"
echo >&2 "Re-run as \"bash $CMD\" for proper operation"
exit 1
;;
esac
Bạn có thể bỏ qua kiểm tra chức năng hơi hoang tưởng cho các tính năng trong trường hợp đầu tiên và chỉ cần giả định rằng các phiên bản Bash trong tương lai sẽ tương thích.
Không có câu trả lời nào hoạt động với fish
shell (nó không có các biến $$
hoặc$0
).
Này hoạt động đối với tôi (thử nghiệm trên sh
, bash
, fish
, ksh
, csh
, true
, tcsh
, và zsh
; openSUSE 13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
Lệnh này xuất ra một chuỗi như bash
. Ở đây tôi chỉ sử dụng ps
, tail
và sed
(không có phần mở rộng GNU; hãy thử thêm --posix
để kiểm tra). Chúng đều là các lệnh POSIX tiêu chuẩn. Tôi chắc chắn tail
có thể được gỡ bỏ, nhưng sed
fu của tôi không đủ mạnh để làm điều này.
Dường như với tôi, giải pháp này không dễ mang theo vì nó không hoạt động trên OS X. :(
sed: invalid option -- 'E'
trên bash 3.2.51 và tcsh 6.15.00
Giải pháp của tôi:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
Điều này nên được di động trên các nền tảng và vỏ khác nhau. Nó sử dụng ps
như các giải pháp khác, nhưng nó không dựa vào sed
hoặc awk
lọc rác từ đường ống vàps
chính nó để vỏ luôn luôn là mục cuối cùng. Bằng cách này, chúng ta không cần phải dựa vào các biến PID không di động hoặc chọn ra các dòng và cột phù hợp.
Tôi đã thử nghiệm trên Debian và macOS với Bash, Z shell ( zsh
) và fish (không hoạt động với hầu hết các giải pháp này mà không thay đổi biểu thức dành riêng cho cá, vì nó sử dụng biến PID khác nhau).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
là không đáng tin cậy. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
là tốt hơn, nhưng tại sao không chỉ sử dụng ps -p $$
?
Trên Mac OS X (và FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
, và nó đã cho tôi -bash
.
mutt
...: -b
Cắt xén PID từ đầu ra của "ps" là không cần thiết, bởi vì bạn có thể đọc dòng lệnh tương ứng cho bất kỳ PID nào từ cấu trúc thư mục / Proc:
echo $(cat /proc/$$/cmdline)
Tuy nhiên, điều đó có thể không tốt hơn chỉ đơn giản là:
echo $0
Về việc chạy một shell thực sự khác với tên chỉ ra, một ý tưởng là yêu cầu phiên bản từ shell sử dụng tên bạn đã nhận trước đó:
<some_shell> --version
sh
dường như thất bại với mã thoát 2 trong khi những người khác cung cấp một cái gì đó hữu ích (nhưng tôi không thể xác minh tất cả vì tôi không có chúng):
$ sh --version
sh: 0: Illegal option --
echo $?
2
Đây không phải là một giải pháp rất sạch sẽ, nhưng nó làm những gì bạn muốn.
# MUST BE SOURCED..
getshell() {
local shell="`ps -p $$ | tail -1 | awk '{print $4}'`"
shells_array=(
# It is important that the shells are listed in descending order of their name length.
pdksh
bash dash mksh
zsh ksh
sh
)
local suited=false
for i in ${shells_array[*]}; do
if ! [ -z `printf $shell | grep $i` ] && ! $suited; then
shell=$i
suited=true
fi
done
echo $shell
}
getshell
Bây giờ bạn có thể sử dụng $(getshell) --version
.
Mặc dù vậy, công việc này chỉ hoạt động trên các vỏ giống như KornShell (ksh).
dash
, yash
vv Thông thường, nếu bạn đang sử dụng bash
, zsh
, ksh
, bất cứ điều gì - bạn không nên quan tâm đến những việc như vậy.
ksh
bộ tính năng chủ yếu là một tập hợp các bash
tính năng (mặc dù chưa kiểm tra kỹ điều này).
Thực hiện như sau để biết liệu shell của bạn có đang sử dụng Dash / Bash hay không.
ls –la /bin/sh
:
nếu kết quả là /bin/sh -> /bin/bash
==> Sau đó, shell của bạn đang sử dụng Bash.
nếu kết quả là /bin/sh ->/bin/dash
==> Sau đó, shell của bạn đang sử dụng Dash.
Nếu bạn muốn thay đổi từ Bash thành Dash hoặc ngược lại, hãy sử dụng mã dưới đây:
ln -s /bin/bash /bin/sh
(đổi vỏ thành Bash)
Lưu ý : Nếu lệnh trên dẫn đến lỗi, / bin / sh đã tồn tại, hãy xóa / bin / sh và thử lại.
Vui lòng sử dụng lệnh dưới đây:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
làm những gì bạn đang cố gắng làm và làm nó tốt. Thứ hai cũng không tốt, vì $SHELL
biến môi trường chứa shell mặc định cho người dùng hiện tại, không phải shell hiện đang chạy. Nếu tôi có ví dụ bash
được đặt làm shell mặc định, hãy thực thi zsh
và echo $SHELL
nó sẽ in bash
.
echo $SHELL
có thể là rác: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Cái này hoạt động tốt trên Red Hat Linux (RHEL), macOS, BSD và một số AIXes :
ps -T $$ | awk 'NR==2{print $NF}'
cách khác, cái sau đây cũng sẽ hoạt động nếu pstree có sẵn,
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
thay thế không?) Có thể dễ mang theo hơn là tìm tên của vỏ. Phong tục địa phương có thể khiến bạn chạy một cái gì đó có tên/bin/sh
thực sự có thể là tro, gạch ngang, bash, v.v.