Как я могу определить текущую оболочку, над которой я работаю?
Достаточно ли вывода команды ps
?
Как это можно сделать в разных вариантах UNIX?
Как я могу определить текущую оболочку, над которой я работаю?
Достаточно ли вывода команды ps
?
Как это можно сделать в разных вариантах UNIX?
Существует 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
.
ps -p $$
должен работать в любом месте, где выполняются решения с участием ps -ef
и grep
(в любом варианте Unix, который поддерживает параметры POSIX для ps
) и не будет страдать от ложных срабатываний, введенных grepping для последовательности цифр, которые могут появляться в другом месте.
Try
ps -p $$ -oargs=
или
ps -p $$ -ocomm=
Если вы просто хотите, чтобы пользователь вызывал script с помощью bash:
if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi
Вы можете попробовать:
ps | grep `echo $$` | awk '{ print $4 }'
Или:
echo $SHELL
$SHELL
не всегда нужно отображать текущую оболочку. Он отображает только оболочку по умолчанию, которую нужно вызвать.
Чтобы проверить выше, Say bash
является оболочкой по умолчанию, попробуйте echo $SHELL
, затем в том же терминале, перейдите в другую оболочку (например, ksh) и попробуйте $SHELL
, вы увидите результат как bash в обоих случаях.
Чтобы получить имя текущей оболочки, используйте cat /proc/$$/cmdline
И путь к исполняемому файлу оболочки readlink /proc/$$/exe
ps - самый надежный метод. Экран SHELL не может быть установлен, и даже если он есть, его можно легко подделать
У меня есть простой трюк, чтобы найти текущую оболочку. Просто введите случайную строку (которая не является командой). Он потерпит неудачу и вернет ошибку "не найден", но в начале строки он скажет, какая оболочка это:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
Это всегда даст фактическую используемую оболочку - получает имя фактического исполняемого файла, а не имя оболочки (т.е. ksh93
вместо ksh
и т.д.). Для /bin/sh
будет отображаться фактическая используемая оболочка: т.е. dash
ls -l /proc/$$/exe | sed 's%.*/%%'
Я знаю, что многие говорят, что вывод ls
должен обрабатываться по-новому, но какова вероятность того, что у вас будет оболочка, которую вы используете с именами со специальными символами или поместите в каталог с именами со специальными символами? Если это так, то есть много других примеров, делающих это по-другому.
ОБНОВЛЕНИЕ: как указал Тоби Спейт, это был бы более правильный и более чистый способ достижения того же самого:
basename $(readlink /proc/$$/exe)
Я пробовал много разных подходов, и лучший для меня:
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 -p $$ | awk '$1 == PP {print $4}' PP=$$
Зачем запускать ненужные приложения, когда "awk" может сделать это за вас?
Если ваш /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 "[email protected]" | 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}'
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' #use the PID to see what the process is.
Ни один из ответов не работал с оболочкой 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.: (
Существует множество способов узнать оболочку и ее соответствующую версию. Вот несколько из них, которые работали для меня.
Прямо вперед
Хакский подход
$ > ******* (Введите набор случайных символов, а на выходе вы получите имя оболочки. В моем случае - bash: chapter2-a-sample-isomorphic-app: command not найдено)
В Mac OS X (& FreeBSD):
ps -p $$ -axco command | sed -n '$p'
Если вы просто хотите проверить, что вы работаете (конкретная версия) Bash,
лучший способ сделать это - использовать переменную массива $BASH_VERSINFO
.
Как переменная массива (только для чтения) она не может быть установлена в среде,
поэтому вы можете быть уверены, что он придет (если вообще) из текущей оболочки.
Однако, поскольку Bash имеет другое поведение при вызове sh
,
вам также нужно проверить, что переменная среды $BASH
заканчивается на /bash
.
В script я написал, что использует имена функций с -
(не подчеркивает)
и зависит от ассоциативных массивов (добавлено в Bash 4),
У меня есть следующая проверка работоспособности (с полезным сообщением об ошибке пользователя):
case `eval 'echo [email protected]${BASH_VERSINFO[0]}' 2>/dev/null` in
*/[email protected][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
;;
*/[email protected][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 будут совместимы.
Grepping PID с выхода "ps" не требуется, потому что вы можете прочитать соответствующую командную строку для любой структуры каталога PID из /proc:
echo $(cat /proc/$$/cmdline)
Однако это может быть не лучше, чем просто:
echo $0
О запуске фактически другой оболочки, чем указано в названии, одна идея состоит в том, чтобы запросить версию из оболочки с использованием ранее полученного имени:
<some_shell> --version
sh, похоже, терпит неудачу с кодом выхода 2, а другие дают что-то полезное (но я не могу проверить все, так как у меня их нет):
$ sh --version
sh: 0: Illegal option --
echo $?
2
Это не очень чистое решение, но делает то, что вы хотите.
Я понимаю, что ответ немного поздний в этом добром старом 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-подобных оболочек.
Мое решение:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
Это должно быть переносимо для разных платформ и оболочек. Он использует ps
как другие решения, но не полагается на sed
или awk
и отфильтровывает мусор из трубопровода и ps
сам, поэтому оболочка всегда должна быть последней записью. Таким образом, нам не нужно полагаться на непереносимые переменные PID или выбирать правильные строки и столбцы.
Я тестировал на Debian и MacOS с помощью bash, zsh и fish (что не работает с большинством этих решений без изменения выражения специально для рыбы, поскольку оно использует другую переменную PID).
Чтобы узнать, использует ли ваша оболочка DASH/BASH, выполните следующие действия.
1) 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 и повторите попытку.
Пожалуйста, используйте следующую команду:
# ps -p $$ | tail -1 | awk '{print $4}'
Этот хорошо работает на RHEL, MacOS, BSD и некоторых AIX
ps -T $$ | awk 'NR==2{print $NF}'
альтернативно, следующий также должен работать, если pstree доступен,
pstree | egrep $$ | awk 'NR==2{print $NF}'