[英]How to determine the current interactive shell that I'm in (command-line)
如何确定我正在使用的当前 shell?
仅ps
命令的输出就足够了吗?
如何在不同风格的 Unix 中做到这一点?
有三种方法可以找到当前 shell 的可执行文件的名称:
请注意,如果 shell 的可执行文件是/bin/sh
,则所有三种方法都可能被愚弄,但它实际上是重命名的bash
,例如(经常发生)。
因此,您关于ps
输出是否会起作用的第二个问题是用“不总是”来回答的。
echo $0
- 将打印程序名称...在外壳的情况下是实际外壳。
ps -ef | grep $$ | grep -v grep
ps -ef | grep $$ | grep -v grep
- 这将在正在运行的进程列表中查找当前进程 ID。 由于当前进程是shell,所以会包含在内。
这不是 100% 可靠的,因为您可能有其他进程的ps
列表包含与 shell 的进程 ID 相同的数字,特别是如果该 ID 是一个小数字(例如,如果 shell 的 PID 为“5”,您可能会找到进程在同一grep
输出中称为“java5”或“perl5”!)。 这是“ps”方法的第二个问题,除了不能依赖 shell 名称之外。
echo $SHELL
- 当前 shell 的路径存储为任何 shell 的SHELL
变量。 对此的警告是,如果您将 shell 显式启动为子进程(例如,它不是您的登录 shell),您将获得登录 shell 的值。 如果有可能,请使用ps
或$0
方法。
但是,如果可执行文件与您的实际 shell 不匹配(例如/bin/sh
实际上是 bash 或 ksh),您需要启发式方法。 下面是一些特定于各种 shell 的环境变量:
$version
在 tcsh 上设置
$BASH
在 bash 上设置
$shell
(小写)设置为 csh 或 tcsh 中的实际 shell 名称
$ZSH_NAME
在 zsh 上设置
ksh 有$PS3
和$PS4
集,而普通的 Bourne shell ( sh
) 只有$PS1
和$PS2
集。 这通常似乎是最难区分的 - 我们在 Solaris boxen 上安装的sh
和ksh
之间的整个环境变量集的唯一区别是$ERRNO
、 $FCEDIT
、 $LINENO
、 $PPID
、 $PS3
、 $PS4
、 $RANDOM
、 $SECONDS
和$TMOUT
。
ps -p $$
应该在涉及ps -ef
和grep
的解决方案所做的任何地方工作(在任何支持ps
的 POSIX 选项的Unix 变体上),并且不会受到通过 grepping 对可能出现在其他地方的数字序列引入的误报的影响。
尝试
ps -p $$ -oargs=
或者
ps -p $$ -ocomm=
如果您只想确保用户使用 Bash 调用脚本:
if [ -z "$BASH" ] ;then echo "Please run this script $0 with bash"; exit 1; fi
你可以试试:
ps | grep `echo $$` | awk '{ print $4 }'
或者:
echo $SHELL
$SHELL
不需要总是显示当前的 shell。 它仅反映要调用的默认 shell。
要测试上述内容,假设bash
是默认 shell,尝试echo $SHELL
,然后在同一个终端中,进入其他 shell(例如KornShell (ksh))并尝试$SHELL
。 在这两种情况下,您都会看到 bash 的结果。
要获取当前 shell 的名称,请使用cat /proc/$$/cmdline
。 以及通过readlink /proc/$$/exe
shell 可执行文件的路径。
有很多方法可以找出 shell 及其对应的版本。 这里有一些对我有用的。
直截了当
骇人听闻的方法
$> ******* (输入一组随机字符,在输出中您将获得 shell 名称。在我的情况下-bash: chapter2-a-sample-isomorphic-app: command not found )
我有一个简单的技巧来找到当前的 shell。 只需键入一个随机字符串(这不是命令)。 它将失败并返回“未找到”错误,但在行首它会说它是哪个 shell:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
ps是最可靠的方法。 不保证会设置 SHELL 环境变量,即使设置了,也很容易被欺骗。
我尝试了许多不同的方法,对我来说最好的方法是:
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 下进行了测试。
以下将始终给出实际使用的 shell - 它获取实际可执行文件的名称而不是 shell 名称(即ksh93
而不是ksh
等)。 对于/bin/sh
,它将显示实际使用的 shell,即dash
。
ls -l /proc/$$/exe | sed 's%.*/%%'
我知道有很多人说永远不应该处理ls
输出,但是您使用的外壳以特殊字符命名或放置在以特殊字符命名的目录中的概率是多少? 如果情况仍然如此,那么还有很多其他不同做法的例子。
正如Toby Speight所指出的,这将是实现相同目标的更合适和更清洁的方法:
basename $(readlink /proc/$$/exe)
我打印父进程的变体:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
当 AWK 可以为您完成时,不要运行不必要的应用程序。
假设您的/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}'
如果您只想检查您是否正在运行(特定版本的)Bash,最好的方法是使用$BASH_VERSINFO
数组变量。 作为(只读)数组变量,它不能在环境中设置,因此您可以确定它(如果有的话)来自当前 shell。
但是,由于 Bash 在以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
(它没有变量$$
或$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
fu 不够强大,无法做到这一点。
在我看来,这个解决方案不是很便携,因为它在 OS X 上不起作用。:(
这不是一个非常干净的解决方案,但它可以满足您的需求。
# 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)。
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
执行以下操作以了解您的 shell 是否使用 Dash/Bash。
ls –la /bin/sh
:
如果结果是/bin/sh -> /bin/bash
==> 那么你的 shell 正在使用 Bash。
如果结果是/bin/sh ->/bin/dash
==> 那么你的 shell 正在使用 Dash。
如果您想从 Bash 更改为 Dash 或反之亦然,请使用以下代码:
ln -s /bin/bash /bin/sh
(将 shell 更改为 Bash)
注意:如果上述命令导致错误提示 /bin/sh 已存在,请删除 /bin/sh 并重试。
我特别喜欢Nahuel Fouilleul 的解决方案,但我必须在带有内置 Bash shell 的Ubuntu 18.04 (Bionic Beaver) 上运行它的以下变体:
bash -c 'shellPID=$$; ps -ocomm= -q $shellPID'
没有临时变量shellPID
,例如:
bash -c 'ps -ocomm= -q $$'
只会为我输出ps
。 也许您并不都使用非交互模式,这会有所不同。
不需要从“ps”的输出中提取 PID,因为您可以从 /proc 目录结构中读取任何 PID 的相应命令行:
echo $(cat /proc/$$/cmdline)
但是,这可能并不比简单地更好:
echo $0
关于运行与名称所指示的实际不同的 shell,一个想法是使用您之前获得的名称从 shell 请求版本:
<some_shell> --version
sh
似乎因退出代码 2 而失败,而其他人则提供了一些有用的东西(但我无法验证所有内容,因为我没有它们):
$ sh --version
sh: 0: Illegal option --
echo $?
2
在 Mac OS X(和 FreeBSD)上:
ps -p $$ -axco command | sed -n '$p'
请使用以下命令:
ps -p $$ | tail -1 | awk '{print $4}'
您可以使用echo $SHELL|sed "s/\/bin\///g"
这个在Red Hat Linux (RHEL)、macOS、BSD 和一些AIX上运行良好:
ps -T $$ | awk 'NR==2{print $NF}'
或者,如果pstree可用,以下一个也应该工作,
pstree | egrep $$ | awk 'NR==2{print $NF}'
我想出了这个:
sed 's/.*SHELL=//; s/[[:upper:]].*//' /proc/$$/environ
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.