Как определить текущую оболочку, над которой я работаю?

445

Как я могу определить текущую оболочку, над которой я работаю?

Достаточно ли вывода команды ps?

Как это можно сделать в разных вариантах UNIX?

  • 8
    Тестирование конкретных возможностей (например, выполняет ли ! Замена?), Вероятно, более переносимо, чем поиск имени оболочки. В локальном обычае вы можете запустить что-то с именем /bin/sh которое может быть ash, dash, bash и т.д.
  • 0
    @msw = +1. (см. мой ответ для возможных подходов к тестированию)
Показать ещё 3 комментария
Теги:
tcsh
csh

21 ответ

545
Лучший ответ
  • Существует 3 подхода к поиску имени текущего исполняемого файла оболочки:

    Обратите внимание, что все три подхода могут быть обмануты, если исполняемый файл оболочки /bin/sh, но он действительно переименован, например, (t21) (что часто происходит).

    Таким образом, ваш второй вопрос о том, будет ли выводиться вывод ps, будет отвечать " не всегда".

    • echo $0 - напечатает имя программы... которое в случае оболочки является реальной оболочкой

    • ps -ef | grep $$ | grep -v grep - Это будет искать текущий идентификатор процесса в списке запущенных процессов. Поскольку текущий процесс является оболочкой, он будет включен.

      Это не на 100% надежнее, так как у вас могут быть ДРУГИЕ процессы, чей список ps содержит тот же номер, что и идентификатор процесса оболочки, особенно если этот идентификатор является небольшим # (например, если PID оболочки равен "5", вы можете найти процессы, называемые "java5" или "perl5", в том же выпуске grep!). Это вторая проблема с подходом "ps", не имея возможности полагаться на имя оболочки.

    • echo $SHELL - Путь к текущей оболочке сохраняется как переменная SHELL для любой оболочки. Предостережение для этого заключается в том, что если вы запускаете оболочку явно как подпроцесс (например, это не ваша оболочка для входа), вы получите вместо этого значение своей учетной записи. Если это возможно, используйте подход ps или $0.


  • Если, однако, исполняемый файл не соответствует вашей реальной оболочке (например, /bin/sh на самом деле bash или ksh), вам нужна эвристика. Вот некоторые экологические переменные, характерные для разных оболочек:

    • $version устанавливается на tcsh

    • $BASH установлен на bash

    • $shell (в нижнем регистре) устанавливается фактическое имя оболочки в csh или tcsh

    • $ZSH_NAME установлен на zsh

    • ksh имеет $PS3 и $PS4 set, тогда как обычная оболочка Bourne (sh) имеет только $PS1 и $PS2. Это, как правило, кажется наиболее сложным - единственная разница во всем наборе переменных envionmental между sh и ksh, которые мы установили на Solaris boxen, - это $ERRNO, $FCEDIT, $LINENO, $PPID, $PS3, $PS4, $RANDOM, $SECONDS, $TMOUT.

  • 0
    $ {. sh.version} установлен на ksh93
  • 11
    ps -p $$ как указывает Мэтью Слэттери . Для ksh : echo $KSH_VERSION или echo ${.sh.version} .
Показать ещё 12 комментариев
72

ps -p $$

должен работать в любом месте, где выполняются решения с участием ps -ef и grep (в любом варианте Unix, который поддерживает параметры POSIX для ps) и не будет страдать от ложных срабатываний, введенных grepping для последовательности цифр, которые могут появляться в другом месте.

  • 9
    Некоторые оболочки имеют свои собственные встроенные версии ps которые могут не понимать -p поэтому вам может потребоваться использовать /bin/ps -p $$ .
  • 9
    Все оболочки, с которыми я знаком, понимают $$ за исключением fish с которой вам придется использовать ps -p %self .
Показать ещё 2 комментария
30

Try

ps -p $$ -oargs=

или

ps -p $$ -ocomm=
  • 3
    Это хороший короткий. Я использовал себя ps -o fname --no-headers $$ .
  • 0
    Благодарю. Я обнаружил, что это лучший вариант для использования в скрипте для защиты команд bash test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash . (Следующая строка в моем скрипте имеет эквивалент для csh.)
Показать ещё 1 комментарий
18

Вы можете попробовать:

ps | grep `echo $$` | awk '{ print $4 }'

Или:

echo $SHELL
  • 8
    Какой смысл `echo $$` , когда просто $$ подойдет?
  • 5
    Какой смысл в grep после awk, когда /pattern/ { action } будет делать?
Показать ещё 4 комментария
17

Если вы просто хотите, чтобы пользователь вызывал script с помощью bash:

if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi
  • 1
    Это должно быть ближе к вершине. Большое спасибо.
  • 2
    Это не должно быть ближе к вершине, потому что это не отвечает на вопрос вообще. Если вопрос будет «Как проверить, работает ли скрипт под Bash», я голосую за него.
Показать ещё 3 комментария
10

$SHELL не всегда нужно отображать текущую оболочку. Он отображает только оболочку по умолчанию, которую нужно вызвать.

Чтобы проверить выше, Say bash является оболочкой по умолчанию, попробуйте echo $SHELL, затем в том же терминале, перейдите в другую оболочку (например, ksh) и попробуйте $SHELL, вы увидите результат как bash в обоих случаях.

Чтобы получить имя текущей оболочки, используйте cat /proc/$$/cmdline И путь к исполняемому файлу оболочки readlink /proc/$$/exe

  • 4
    ... При условии, что у вас есть /proc .
8

ps - самый надежный метод. Экран SHELL не может быть установлен, и даже если он есть, его можно легко подделать

  • 11
    +1 $ SHELL - оболочка по умолчанию для программ, которые должны ее породить. Это не обязательно отражает оболочку, которая в данный момент работает.
7

У меня есть простой трюк, чтобы найти текущую оболочку. Просто введите случайную строку (которая не является командой). Он потерпит неудачу и вернет ошибку "не найден", но в начале строки он скажет, какая оболочка это:

ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
  • 1
    Ничего хорошего в сценарии. echo 'aaaa' > script; chmod +x script; ./script дает ./script.sh: 1: aaaa: not found
7

Это всегда будет использовать фактическую используемую оболочку - получает имя фактического исполняемого файла, а не имя оболочки (т.е. ksh93 вместо ksh и т.д.). Для /bin/sh будет отображаться фактическая используемая оболочка: ie dash

ls -l /proc/$$/exe | sed 's%.*/%%'

Я знаю, что многие из тех, кто говорит, что ls вывод должен быть обработан новее, но какова вероятность того, что у вас будет оболочка, которую вы используете именовали со специальными символами или помещена в каталог с особыми символами? Если все-таки это так, здесь есть множество других примеров, которые делают это по-другому.

  • 2
    Это просто ошибки на всех Unices, которые не предоставляют /proc . Не все в мире Linux box.
  • 4
    И если вы работаете в Linux, мы бы предпочли basename $(readlink /proc/$$/exe) ls + sed + echo .
Показать ещё 1 комментарий
4

Мой вариант при печати родительского процесса.

ps -p $$ | awk '$1 == PP {print $4}' PP=$$

Зачем запускать ненужные приложения, когда "awk" может сделать это за вас?

2

Я пробовал много разных подходов, и лучший для меня:

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.

  • 0
    На мой взгляд, (Cygwin | Windows 7) ваш ответ самый лучший, и ps -p $$ | хвост -1 | gawk '{print $ NF}' работает даже из cmd без $ bash. Обратите внимание на gawk вместо awk, так как awk работает только с bash.
  • 0
    @WebComer Извините, я не уверен, что вы подразумеваете под " работает даже из cmd без $ bash ". Даже если у вас есть порты Windows ps , tail и gawk , cmd не определяет $$ поскольку это PID, поэтому он определенно не может работать под простым cmd.
2

Если ваш /bin/sh поддерживает стандарт POSIX, и ваша система имеет установленную команду lsof - возможная альтернатива lsof в этом случае может быть pid2path - вы также можете использовать (или адаптировать) следующие script, который печатает полные пути:

#!/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}'
  • 0
    это работает в рыбе! но для меня это не сработает в bash, потому что опция -i (-> ignore environment) lsof env в строке, где вы проверяете, доступен ли lsof . происходит сбой с: env -i PATH="${PATH}" type lsof -> env: 'type': No such file or directory
1

Это не очень чистое решение, но делает то, что вы хотите.

Я понимаю, что ответ немного поздний в этом добром старом 2015 году, но...

#MUST BE SOURCED..
getshell() {
    local shell="`ps -p $$ | tail -1 | awk '{print $4}'`"

    shells_array=(
    # It is important that the shells are listed by the decrease of their length name.
        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.

Это работает, однако, только для ksh-подобных оболочек.

  • 0
    «Это работает, однако, только на ksh-подобных оболочках». Вы говорите, что я должен проверить оболочку перед запуском этого? Хм ...
  • 0
    @jpaugh, списки должны поддерживаться оболочкой, исходящей из этого кода, что не относится к dash , yash и т. д. Обычно, если вы используете bash , zsh , ksh , вам не следует беспокоиться о таких вещах.
Показать ещё 2 комментария
1

Grepping PID с выхода "ps" не требуется, потому что вы можете прочитать соответствующую командную строку для любой структуры каталога PID из /proc:

echo $(cat /proc/$$/cmdline)

Однако это может быть не лучше, чем просто:

echo $0

О запуске фактически другой оболочки, чем указано в названии, одна идея состоит в том, чтобы запросить версию из оболочки с использованием ранее полученного имени:

<some_shell> --version

sh, похоже, терпит неудачу с кодом выхода 2, а другие дают что-то полезное (но я не могу проверить все, так как у меня их нет):

$ sh --version
sh: 0: Illegal option --
echo $?
2
1

Ни один из ответов не работал с оболочкой 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 extesions, попробуйте добавить --posix, чтобы проверить его). Все они являются стандартными командами POSIX. Я уверен, что tail можно удалить, но мой sed fu недостаточно силен, чтобы сделать это.

Мне кажется, что это решение не очень портативно, так как оно не работает на OS X.: (

  • 0
    Я получаю sed: invalid option -- 'E' на bash 3.2.51 и tcsh 6.15.00
1

Если вы просто хотите проверить, что вы работаете (конкретная версия) Bash, лучший способ сделать это - использовать переменную массива $BASH_VERSINFO. Как переменная массива (только для чтения) она не может быть установлена ​​в среде, поэтому вы можете быть уверены, что он придет (если вообще) из текущей оболочки. Однако, поскольку Bash имеет другое поведение при вызове sh, вам также нужно проверить, что переменная среды $BASH заканчивается на /bash.

В script я написал, что использует имена функций с - (не подчеркивает) и зависит от ассоциативных массивов (добавлено в 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 будут совместимы.

1

В Mac OS X (& FreeBSD):

ps -p $$ -axco command | sed -n '$p' 
  • 1
    Попробовал это при использовании zsh , и он дал мне -bash .
1
echo $$ # Gives the Parent Process ID 
ps -ef | grep $$ | awk '{print $8}' #use the PID to see what the process is.

from http://www.unix.com/unix-dummies-questions-answers/10390-how-do-you-know-what-your-current-shell.html

  • 3
    Это не родительский процесс - это текущий процесс.
  • 6
    Использование grep $$ ненадежно. ps -ef | awk -v pid=$$ '$2==pid { print $8 }' лучше, но почему бы просто не использовать ps -p $$ ?
Показать ещё 1 комментарий
0

Существует множество способов узнать оболочку и ее соответствующую версию. Вот несколько из них, которые работали для меня.

Прямо вперед

  • $ > echo $0 (дает вам имя программы. В моем случае выход был - bash)
  • $ > $SHELL (Это приведет вас в оболочку и в приглашении вы получите имя и версию оболочки. В моем случае bash3.2 $)
  • $ > echo $SHELL (Это даст вам исполняемый путь. В моем случае /bin/ bash)
  • $ > $SHELL --version (Это даст полную информацию о программном обеспечении оболочки с типом лицензии)

Хакский подход

$ > ******* (Введите набор случайных символов, а на выходе вы получите имя оболочки. В моем случае - bash: chapter2-a-sample-isomorphic-app: command not найдено)

0

Мое решение:

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

Это должно быть переносимо для разных платформ и оболочек. Он использует ps как другие решения, но не полагается на sed или awk и отфильтровывает мусор из трубопровода и ps сам, поэтому оболочка всегда должна быть последней записью. Таким образом, нам не нужно полагаться на непереносимые переменные PID или выбирать правильные строки и столбцы.

Я тестировал на Debian и MacOS с помощью bash, zsh и fish (что не работает с большинством этих решений без изменения выражения специально для рыбы, поскольку оно использует другую переменную PID).

0

Пожалуйста, используйте следующую команду:

 # ps -p $$ | tail -1 | awk '{print $4}'
  • 0
    Во-первых, ерунда, echo $SHELL делает то, что вы пытаетесь сделать, и делаете это хорошо. Второй также не годится, потому что переменная окружения $SHELL содержит оболочку по умолчанию для текущего пользователя, а не текущую оболочку. Если я, например, установил bash качестве оболочки по умолчанию, выполнил zsh и echo $SHELL , он напечатает bash .
  • 0
    Вы правы, Давид Ференчи. Мы можем использовать команду ps для определения текущей оболочки. [# ps -p $$ | хвост -1 | awk '{print $ 4}'].
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню