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

634

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

Будет ли выход ps команды?

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

мистифицировать
источник
9
Тестирование определенных возможностей (например, выполняет ли !подстановка?), Вероятно, более переносимо, чем поиск имени оболочки. По местным обычаям вы можете запускать что-то по имени, /bin/shкоторое на самом деле может быть ash, dash, bash и т. Д.
msw,
1
@msw: похоже на хороший комментарий, но это заставляет меня задуматься: «Как?».
Нобар
Похоже, что нет простого ответа на этот вопрос. Если мы не можем запросить оболочку, возможно, лучший подход - всегда указывать оболочку. Я не уверен, что это всегда возможно, но, возможно, это легче сделать, чем обычно думают люди.
Нобар
Это может помочь -> opensourceforgeeks.blogspot.in/2013/05/…
Аникет Тхакур
@Aniket, не так много помощи, как вы думаете - это интересует только интерактивные процессы оболочки.
Тоби Спейт

Ответы:

794
  • Есть три подхода к поиску имени исполняемого файла текущей оболочки:

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

    Таким образом, на ваш второй вопрос о том, psподойдет ли вывод, ответ « не всегда ».

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

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

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

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


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

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

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

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

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

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

DVK
источник
$ {. sh.version} установлен на ksh93
fpmurphy
14
ps -p $$как указывает Мэтью Слэттери . Для ksh: echo $KSH_VERSIONили echo ${.sh.version}.
Приостановлено до дальнейшего уведомления.
@Dennish - мой ksh сейчас не имеет установленного KSH_VERSION. и echo ${.sh.version}возвращает «Bad Substitution». Смотрите мое решение выше
ДВК
3
« ps -ef | grep …… Это не на 100% надежно, как…» Использование простого регулярного выражения с помощью egrepили grep -eможет легко довести надежность до 100%: для всех целей ps -ef | egrep "^\s*\d+\s+$$\s+". Он ^гарантирует, что мы начинаем с начала строки, \d+потребляет UID, $$совпадает с PID, а также \s*и \s+учитывают и обеспечивают пробел между другими частями.
Слипп Д. Томпсон
2
@ SlippD. Томпсон не работал в GNU / Linux. Но это похоже на работу:ps -ef | awk '$2==pid' pid=$$
Ярно
98

ps -p $$

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

Мэтью Слэттери
источник
12
Некоторые оболочки имеют свои собственные встроенные версии, psкоторые могут не понимать, -pпоэтому вам может потребоваться использовать /bin/ps -p $$.
Приостановлено до дальнейшего уведомления.
13
Все снаряды, с которыми я знаком, понимают, $$за исключением того, fishчто вам придется использовать ps -p %self.
Приостановлено до дальнейшего уведомления.
3
На самом деле, вы не должны полагаться на сложные пути, такие как /bin/ps. psможет быть легко (на самом деле это вполне нормально в настоящее время) может быть установлен в /usr/bin. $(which ps) -p $$это лучший способ. Конечно, это не будет работать в рыбе и, возможно, в некоторых других оболочках. Я думаю, что это (which ps) -p %selfв рыбе.
Арон Седерхольм,
1
В некоторых минимальных системах, таких как докерский контейнер debian-slim, ps может и не быть. В этом случае этот подход все еще работает:readlink /proc/$$/exe
mxmlnkn
если ваш shэмулируется bash, ps -p даст вам, /usr/bin/bashдаже если вы запустите его какsh
Ding-Yi Chen
45

Пытаться

ps -p $$ -oargs=

или

ps -p $$ -ocomm=
Науэль Фуйе
источник
4
Это хороший короткий. Я использовал себя ps -o fname --no-headers $$.
Анн ван Россум
Спасибо. Я обнаружил, что это лучший вариант для использования в скрипте для защиты определенных команд bash test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash. (Следующая строка в моем скрипте имеет эквивалент для csh.)
craq
1
Я обнаружил, что если вы делаете это изнутри, то это может привести к ложным дополнительным строкам путем сопоставления PID родителя и фактического процесса оболочки. Для этого я использую -qвместо -p:SHELL=$(ps -ocomm= -q $$)
Стив
35

Если вы просто хотите убедиться, что пользователь вызывает скрипт с Bash:

if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi
Питер Ламберг
источник
1
Это должно быть ближе к вершине. Большое спасибо.
Алексей Скрипник,
4
Это не должно быть ближе к вершине, потому что это не отвечает на вопрос вообще. Если вопрос будет «Как проверить, работает ли скрипт под Bash», я голосую за него.
Дэвид Ференци Рогожан,
2
@DawidFerenczy - этот вопрос является лучшим результатом при поиске этой фразы. В конечном счете, я думаю, что гораздо важнее, чтобы ответы отвечали на то, что ищут люди, а не на то, о чем был первоначальный вопрос.
ArtOfWarfare
3
Кроме того, переменный $ BASH будет определен в Tcsh , если Tcsh вызывается из Баша
+18446744073709551615
Это очень полезно, спасибо. Просто адаптировать его немного, поставив его в качестве второй линии после того, как #!/bin/bash: if [ ! -n "$BASH" ] ;then exec bash $0; fi. С этой строкой скрипт запускается с использованием bash, даже если он запускается с использованием ksh или sh. Мой вариант использования не нуждается в аргументах командной строки, но они могут быть добавлены после, $0если это необходимо.
Джоанис
20

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

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

Или:

echo $SHELL
karlphillip
источник
6
Какой смысл в grep, за которым следует awk, когда это /pattern/ { action }будет сделано?
Йенс
# в zshell alias shell = 'echo $ {SHELL: t}'
SergioAraujo
4
$SHELLПеременная окружения содержит оболочку, которая настроена по умолчанию для текущего пользователя. Он не отражает оболочку, которая в данный момент работает. Также лучше использовать, ps -p $$чем grepping $$ из-за ложных срабатываний.
Дэвид Ференци Рогожан,
$ SHELL env. переменная указывает на «родительскую» оболочку, как указано в спецификации POSIX: SHELL Эта переменная должна представлять путь к предпочтительному интерпретатору языка команд пользователя. Поэтому значение $ SHELL может не быть текущей оболочкой.
Тео
все внутри awk,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
go2null
16

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

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

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

sr01853
источник
8
... При условии, что у вас есть /proc.
Трипли
10

PS это самый надежный метод. Не гарантируется, что переменная окружения SHELL будет установлена, и даже если она есть, она может быть легко подделана.

ennuikiller
источник
12
+1 $ SHELL - оболочка по умолчанию для программ, которые должны ее породить. Это не обязательно отражает оболочку, которая в данный момент работает.
Джим Льюис
8

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

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

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

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

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

Как указал Тоби Спейт , это было бы более правильным и более чистым способом достижения того же самого:

basename $(readlink /proc/$$/exe)
vadimbog
источник
3
Это просто ошибки на всех Unices, которые не предоставляют /proc. Не все в мире Linux box.
Дженс
5
И если вы находитесь на Linux, мы бы предпочли , basename $(readlink /proc/$$/exe)чтобы ls+ sed+ echo.
Тоби Спейт
1
Это даст имя фактического исполняемого файла , а не фактической оболочки . Когда фактическая оболочка связана как апплет busybox, скажем ash -> /bin/busybox, это даст / bin / busybox.
stepse
6

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

ps -p $$

Это также работает под Cygwin и не может выдавать ложные срабатывания в качестве подсказки ПИД-регулятора. С некоторой очисткой он выводит только имя исполняемого файла (под Cygwin с путем):

ps -p $$ | tail -1 | awk '{print $NF}'

Вы можете создать функцию, чтобы вам не пришлось ее запоминать:

# Print currently active shell
shell () {
  ps -p $$ | tail -1 | awk '{print $NF}'
}

... а затем просто выполнить shell .

Он был протестирован под Debian и Cygwin.

Давид Ференци Рогожан
источник
На мой взгляд (Cygwin | Windows 7) ваш ответ самый лучший, и ps -p $$ | хвост -1 | gawk '{print $ NF}' работает даже из cmd без $ bash. Обратите внимание на gawk вместо awk, так как awk работает только из bash.
WebComer
@WebComer Извините, я не уверен, что вы подразумеваете под " работает даже из cmd без $ bash ". Даже если у вас есть порты Windows ps, tailи gawk, cmd не определяет $$как его PID, поэтому он определенно не может работать под простым cmd.
Дэвид Ференци Рогожан
Почему не просто ps -p$$ -o comm=? POSIX говорит, что указание всех пустых заголовков полностью подавляет заголовок. Мы по-прежнему терпим неудачу (как и все psответы), когда мы получаем непосредственно исполняемый скрипт (например #!/bin/sh).
Тоби Спейт
5

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

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

Не запускайте ненужные приложения, когда AWK может сделать это за вас.

Мэтью Стир
источник
2
Зачем запускать ненужное awk, когда ps -p "$$" -o 'comm='можешь сделать это за тебя?
Тоби Спейт
5

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

прямой

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

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

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

Деванг Паливал
источник
3

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

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

Однако, поскольку 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 будут совместимы.

Алекс Дюпуй
источник
2

Ни один из ответов не работал с 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фу не достаточно силен, чтобы сделать это.

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

rominf
источник
Я получаю sed: invalid option -- 'E'на bash 3.2.51 и tcsh 6.15.00
craq
2

Мое решение:

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

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

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

wickles
источник
1
echo $$ # Gives the Parent Process ID 
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.

Из Как вы знаете , что ваша текущая скорлупа? ,

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

В Mac OS X (и FreeBSD):

ps -p $$ -axco command | sed -n '$p' 
Zaga
источник
2
Попробовал это при использовании zsh, и это дало мне -bash.
user137369
В моей системе (сейчас), протестированной с bash и dash, это возвращение mutt...: -b
Ф. Хаури
1

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

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

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

echo $0

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

<some_shell> --version

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

$ sh --version
sh: 0: Illegal option --
echo $?
2
ajaaskel
источник
1

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

# 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- подобных оболочках (ksh).

theoden8
источник
1
«Это работает, однако, только на ksh-подобных оболочках». Вы говорите, что я должен проверить оболочку перед запуском этого? Хм ...
jpaugh
@jpaugh, списки должны быть поддержаны оболочки Sourcing этот код, который не относится к dash, и yashт.д. Как правило, если вы используете bash, zsh, ksh, что угодно , - вы не должны заботиться о таких вещах.
theoden8
То есть вы считаете bash "кш-подобным"? Понял. Это имеет больше смысла
jpaugh
@ jpaugh, ну вот что я имел ввиду ksh набор функций - это в основном подмножество bashфункций (хотя я не проверил это полностью).
theoden8
1

Чтобы узнать, использует ли ваша оболочка Dash / Bash, выполните следующие действия.

ls –la /bin/sh:

  • если результат /bin/sh -> /bin/bash==> Тогда ваша оболочка использует Bash.

  • если результат /bin/sh ->/bin/dash==> Тогда ваша оболочка использует Dash.

Если вы хотите перейти с Bash на Dash или наоборот, используйте следующий код:

ln -s /bin/bash /bin/sh (изменить оболочку на Bash)

Запись . Если приведенная выше команда приводит к ошибке, говорящей о том, что / bin / sh уже существует, удалите / bin / sh и повторите попытку.

Шива
источник
0

И я придумал это

sed 's/.*SHELL=//; s/[[:upper:]].*//' /proc/$$/environ
Иван
источник
Это будет работать только на Linux ! Ни MacOS, ни BSD!
Ф. Хаури
-1

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

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

Этот хорошо работает на Red Hat Linux (RHEL), macOS, BSD и некоторых AIX :

ps -T $$ | awk 'NR==2{print $NF}' 

альтернативно, следующий также должен работать, если pstree доступен,

pstree | egrep $$ | awk 'NR==2{print $NF}'
мартин захрубский
источник