如何确定正在使用的当前Shell?
将输出 ps
仅命令就足够了吗?
如何在Unix的不同版本中完成此操作?
如何确定正在使用的当前Shell?
将输出 ps
仅命令就足够了吗?
如何在Unix的不同版本中完成此操作?
Answers:
有三种方法可以找到当前shell可执行文件的名称:
请注意,如果外壳程序的可执行文件是/bin/sh
,则可以欺骗所有这三种方法,但是实际上,它实际上是重命名的bash
(经常发生)。
因此,您的第二个问题,即ps
输出是否会用“ 并非总是 ” 回答。
echo $0
-将打印程序名称...如果是外壳,则为实际外壳。
ps -ef | grep $$ | grep -v grep
-这将在正在运行的进程列表中查找当前进程ID。由于当前进程是外壳程序,因此将包含它。
这不是100%可靠的,因为您可能还有其他进程的ps
列表包含与Shell的进程ID相同的数字,尤其是如果该ID是一个小数字(例如,如果Shell的PID为“ 5”,则您可能会发现称为“ java5”或“ perl5”在同一grep
输出中!)。这是“ ps”方法的第二个问题,除了无法依赖shell名称之外。
echo $SHELL
-当前外壳程序的路径存储SHELL
为任何外壳程序的变量。需要注意的是,如果您明确启动外壳程序作为子进程(例如,它不是您的登录外壳程序),那么您将获得登录外壳程序的值。如果有可能,请使用ps
或$0
方法。
但是,如果可执行文件与您的实际shell不匹配(例如/bin/sh
,实际上是bash或ksh),则需要启发式。以下是一些针对各种外壳的环境变量:
$version
在tcsh上设置
$BASH
受到重击
$shell
(小写)设置为csh或tcsh中的实际shell名称
$ZSH_NAME
在zsh上设置
ksh具有$PS3
and $PS4
set,而普通的Bourne shell(sh
)仅具有$PS1
and $PS2
set。这通常似乎是最难区分-在唯一之间整套环境变量的差异sh
和ksh
我们已经安装在Solaris上Boxen有是$ERRNO
,$FCEDIT
,$LINENO
,$PPID
,$PS3
,$PS4
,$RANDOM
,$SECONDS
,和$TMOUT
。
echo ${.sh.version}
返回“错误替换”。请参阅上方的解决方案
ps -ef | grep …
…这不是100%可靠的……” 使用一个简单的正则表达式可以通过egrep
或grep -e
轻松地将可靠性提高到100%的意图和目的ps -ef | egrep "^\s*\d+\s+$$\s+"
。在^
确保我们从行的开头开始,\d+
吃了UID,则$$
该PID匹配,并且\s*
和\s+
账户及保证其他部分之间的空白。
ps -ef | awk '$2==pid' pid=$$
ps -p $$
应该可以在解决方案涉及ps -ef
和grep
执行的任何地方工作(在任何支持POSIX选项的ps
Unix变体上),并且不会遭受grepping可能出现在其他地方的数字序列引起的误报。
ps
,可能无法理解,-p
因此您可能需要使用/bin/ps -p $$
。
$$
除了fish
必须使用的外壳ps -p %self
。
/bin/ps
。ps
可以很容易地安装(实际上,如今已经很正常了)/usr/bin
。$(which ps) -p $$
是更好的方法。当然,这不适用于鱼,甚至可能还有其他贝壳。我认为是(which ps) -p %self
鱼里的。
readlink /proc/$$/exe
sh
的模仿对象是bash
,ps -p /usr/bin/bash
甚至可以将您的运行方式显示为sh
尝试
ps -p $$ -oargs=
要么
ps -p $$ -ocomm=
ps -o fname --no-headers $$
。
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
。(脚本的下一行与csh等效。)
-q
不是-p
:SHELL=$(ps -ocomm= -q $$)
如果只想确保用户正在使用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
。在此行中,即使使用ksh或sh开始,该脚本也使用bash运行。我的用例不需要命令行参数,但$0
如有必要,可以在后面添加它们。
你可以试试:
ps | grep `echo $$` | awk '{ print $4 }'
要么:
echo $SHELL
/pattern/ { action }
关系是什么,什么时候可以呢?
$SHELL
环境变量包含一个外壳程序,该外壳程序被配置为当前用户的默认设置。它不反映当前正在运行的外壳。而且,ps -p $$
由于误报,使用它比删除$$ 更好。
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
不必总是显示当前的shell。它仅反映要调用的默认外壳程序。
要测试以上内容,请说bash
是默认的shell,请尝试echo $SHELL
,然后在同一终端中,进入其他外壳程序(例如KornShell(ksh))并尝试$SHELL
。在两种情况下,您都将看到结果为bash。
要获取当前shell的名称,请使用cat /proc/$$/cmdline
。到shell可执行文件的路径readlink /proc/$$/exe
。
/proc
。
以下内容将始终给出使用的实际外壳程序-它获取实际可执行文件的名称,而不是外壳程序名称(即ksh93
代替ksh
,等等)。对于/bin/sh
,它将显示实际使用的外壳,即dash
。
ls -l /proc/$$/exe | sed 's%.*/%%'
我知道有很多人说 ls
应对输出进行处理,但是您使用的外壳用特殊字符命名或放在以特殊字符命名的目录中的可能性是多少?如果仍然是这种情况,那么还有许多其他的方法可以做到这一点。
正如Toby Speight所指出的那样,这将是实现这一目标的一种更合适,更清洁的方法:
basename $(readlink /proc/$$/exe)
/proc
。并非全世界都是Linux机器。
basename $(readlink /proc/$$/exe)
到ls
+ sed
+ echo
。
ash -> /bin/busybox
,这将给出/ bin / busybox。
我尝试了许多不同的方法,对我来说最好的方法是:
ps -p $$
它也可以在Cygwin下运行,并且不能作为PID grepping产生误报。经过一些清理,它仅输出一个可执行文件名称(在Cygwin下带有路径):
ps -p $$ | tail -1 | awk '{print $NF}'
您可以创建一个函数,这样就不必记住它:
# Print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
...然后执行shell
。
它已在Debian和Cygwin下进行了测试。
ps
,tail
和gawk
,cmd也未定义$$
为PID,因此它绝对不能在普通cmd下运行。
ps -p$$ -o comm=
?POSIX表示将所有标头指定为空会完全抑制该标头。ps
当我们从直接执行的脚本(例如#!/bin/sh
)获得资源时,我们仍然会失败(就像所有答案一样)。
我在打印父进程时的变体:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
当AWK可以帮您完成时,请不要运行不必要的应用程序。
awk
,何时ps -p "$$" -o 'comm='
可以为您完成?
有很多方法可以找到外壳及其对应的版本。这里有一些对我有用的东西。
直截了当
骇人的方法
$> *******(输入一组随机字符,然后在输出中得到外壳名称。在我的情况下,-bash:Chapter2-a-sample-isomorphic-app:命令未找到)
如果您/bin/sh
支持POSIX标准,并且您的系统已lsof
安装命令(lsof
在这种情况下可以替代该命令pid2path
),则还可以使用(或改编)以下打印完整路径的脚本:
#!/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
(-> ignore environment)选项env
在您检查lsof
是否可用的行中而无法执行bash 。它失败:env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
如果您只是想检查自己是否正在运行Bash(特定版本),最好的方法是使用 $BASH_VERSINFO
数组变量。作为(只读)数组变量,不能在环境中设置它,因此可以确保它(如果有的话)来自当前shell。
但是,由于Bash在按as调用时具有不同的行为sh
,因此您还需要检查$BASH
环境变量以结尾/bash
。
在我写的脚本中,函数名称与-
(而不是下划线)一起使用,并且依赖于关联数组(在Bash 4中添加),我具有以下的健全性检查(带有有用的用户错误消息):
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
在第一种情况下,您可以忽略对功能的偏执狂功能检查,而只是假设将来的Bash版本将兼容。
没有任何答案适用于fish
Shell(它没有变量$$
或$0
)。
这对我的作品(测试上sh
,bash
,fish
,ksh
,csh
,true
,tcsh
,和zsh
; openSUSE的13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
此命令输出类似的字符串bash
。在这里,我仅使用ps
,tail
和sed
(没有GNU扩展;尝试添加--posix
以对其进行检查)。它们都是标准的POSIX命令。我敢肯定tail
可以将其删除,但是我的sed
功夫不足以做到这一点。
在我看来,该解决方案不是很可移植,因为它不能在OS X上使用。
sed: invalid option -- 'E'
上bash 3.2.51和tcsh 6.15.00
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
不可靠。ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
更好,但是为什么不仅仅使用ps -p $$
呢?
在Mac OS X(和FreeBSD)上:
ps -p $$ -axco command | sed -n '$p'
zsh
了一下,它给了我-bash
。
mutt
::
这不是一个很干净的解决方案,但是可以满足您的要求。
# 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
现在您可以使用了$(getshell) --version
。
但是,这仅在类似KornShell的shell(ksh)上有效。
dash
,yash
等等。通常情况下,如果你正在使用bash
,zsh
,ksh
,不管是谁-你不应该在乎这样的事情。
ksh
的功能集主要是的功能的子集bash
(尽管尚未对此进行彻底检查)。
请使用以下命令:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
做您想做的并且做得很好。第二个也不是很好,因为$SHELL
环境变量包含当前用户的默认外壳程序,而不是当前正在运行的外壳程序。例如,如果我将其bash
设置为默认外壳,请执行zsh
和echo $SHELL
,它将显示bash
。
echo $SHELL
可能是垃圾: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
这一个在Red Hat Linux(RHEL),macOS,BSD和一些AIXes上运行良好:
ps -T $$ | awk 'NR==2{print $NF}'
或者,如果pstree可用,则以下方法也应工作,
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
替代?)可能比查找外壳名称更容易携带。本地自定义设置可能会让您运行某些名称/bin/sh
,该名称实际上可能是灰,破折号,bash等