繁体   English   中英

如何确定我所在的当前交互式 shell(命令行)

[英]How to determine the current interactive shell that I'm in (command-line)

如何确定我正在使用的当前 shell?

ps命令的输出就足够了吗?

如何在不同风格的 Unix 中做到这一点?

  • 有三种方法可以找到当前 shell 的可执行文件的名称

    请注意,如果 shell 的可执行文件是/bin/sh ,则所有三种方法都可能被愚弄,但它实际上是重命名的bash ,例如(经常发生)。

    因此,您关于ps输出是否会起作用的第二个问题是用“不总是”来回答的。

    1. echo $0 - 将打印程序名称...在外壳的情况下是实际外壳。

    2. 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 名称之外。

    3. 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 上安装的shksh之间的整个环境变量集的唯一区别是$ERRNO$FCEDIT$LINENO$PPID$PS3$PS4$RANDOM$SECONDS$TMOUT

ps -p $$

应该在涉及ps -efgrep的解决方案所做的任何地方工作(在任何支持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 及其对应的版本。 这里有一些对我有用的。

直截了当

  1. $> echo $0 (给你程序名称。在我的例子中,输出是-bash 。)
  2. $> $SHELL (这将带您进入 shell,并在提示符中获得 shell 名称和版本。在我的情况下为 bash3.2$ 。)
  3. $> echo $SHELL (这将为您提供可执行路径。在我的情况下/bin/bash 。)
  4. $> $SHELL --version (这将提供有关具有许可证类型的 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}'

我的解决方案:

ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1

这应该可以跨不同的平台和外壳移植。 它像其他解决方案一样使用ps ,但它不依赖sedawk并从管道和ps本身中过滤掉垃圾,因此外壳应该始终是最后一个条目。 这样我们就不需要依赖不可移植的 PID 变量或挑选正确的行和列。

我已经在 Debian 和 macOS 上使用 Bash、 Z shell ( zsh ) 和fish进行了测试(如果不更改专门针对 fish 的表达式,它不适用于大多数这些解决方案,因为它使用不同的 PID 变量)。

如果您只想检查您是否正在运行(特定版本的)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 )。

这对我有用(在shbashfishkshcshtruetcshzsh上测试;openSUSE 13.2):

ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'

此命令输出类似bash的字符串。 这里我只使用pstailsed (没有 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' 

使用$SHELL环境变量获取它。 一个简单的sed可以删除路径:

echo $SHELL | sed -E 's/^.*\/([aA-zZ]+$)/\1/g'

输出:

bash

它在macOSUbuntuCentOS上进行了测试。

一种方法是:

ps -p $$ -o exe=

这比使用-o args-o comm在另一个答案中建议的 IMO 更好(例如,这些可能使用一些符号链接,例如/bin/sh指向某个特定的 shell 作为DashBash )。

以上返回可执行文件的路径,但请注意,由于 /usr-merge,可能需要检查多个路径(例如/bin/bash/usr/bin/bash )。

另请注意,上述内容不完全兼容POSIX (POSIX ps没有exe )。

请使用以下命令:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM