Как определить текущую оболочку, над которой я работаю?
Будет ли выход ps
команды?
Как это можно сделать в разных вариантах Unix?
Как определить текущую оболочку, над которой я работаю?
Будет ли выход ps
команды?
Как это можно сделать в разных вариантах Unix?
Ответы:
Есть три подхода к поиску имени исполняемого файла текущей оболочки:
Обратите внимание, что все три подхода могут быть обмануты, если исполняемый файл оболочки есть /bin/sh
, но он действительно переименованbash
, например (что часто случается).
Таким образом, на ваш второй вопрос о том, 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
установлен на баш
$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
.
echo ${.sh.version}
возвращает «Bad Substitution». Смотрите мое решение выше
ps -ef | grep …
… Это не на 100% надежно, как…» Использование простого регулярного выражения с помощью egrep
или grep -e
может легко довести надежность до 100%: для всех целей ps -ef | egrep "^\s*\d+\s+$$\s+"
. Он ^
гарантирует, что мы начинаем с начала строки, \d+
потребляет UID, $$
совпадает с PID, а также \s*
и \s+
учитывают и обеспечивают пробел между другими частями.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
должен работать везде, где задействованы ps -ef
и grep
работают решения (в любом варианте Unix, который поддерживает параметры POSIXps
), и не будет страдать от ложных срабатываний, вызванных поиском последовательности цифр, которая может появиться в другом месте.
ps
которые могут не понимать, -p
поэтому вам может потребоваться использовать /bin/ps -p $$
.
$$
за исключением того, fish
что вам придется использовать ps -p %self
.
/bin/ps
. ps
может быть легко (на самом деле это вполне нормально в настоящее время) может быть установлен в /usr/bin
. $(which ps) -p $$
это лучший способ. Конечно, это не будет работать в рыбе и, возможно, в некоторых других оболочках. Я думаю, что это (which ps) -p %self
в рыбе.
readlink /proc/$$/exe
sh
эмулируется bash
, ps -p даст вам, /usr/bin/bash
даже если вы запустите его какsh
Пытаться
ps -p $$ -oargs=
или
ps -p $$ -ocomm=
ps -o fname --no-headers $$
.
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
. (Следующая строка в моем скрипте имеет эквивалент для csh.)
-q
вместо -p
:SHELL=$(ps -ocomm= -q $$)
Если вы просто хотите убедиться, что пользователь вызывает скрипт с Bash:
if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi
#!/bin/bash
: if [ ! -n "$BASH" ] ;then exec bash $0; fi
. С этой строкой скрипт запускается с использованием bash, даже если он запускается с использованием ksh или sh. Мой вариант использования не нуждается в аргументах командной строки, но они могут быть добавлены после, $0
если это необходимо.
Ты можешь попробовать:
ps | grep `echo $$` | awk '{ print $4 }'
Или:
echo $SHELL
/pattern/ { action }
будет сделано?
$SHELL
Переменная окружения содержит оболочку, которая настроена по умолчанию для текущего пользователя. Он не отражает оболочку, которая в данный момент работает. Также лучше использовать, ps -p $$
чем grepping $$ из-за ложных срабатываний.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
не всегда нужно показывать текущую оболочку. Он отражает только оболочку по умолчанию, которая будет вызвана.
Чтобы проверить вышесказанное, скажем, bash
это оболочка по умолчанию, попробуйте echo $SHELL
, а затем в том же терминале перейдите в другую оболочку (например, KornShell (ksh)) и попробуйте $SHELL
. Вы увидите результат как bash в обоих случаях.
Чтобы получить имя текущей оболочки, используйте cat /proc/$$/cmdline
. И путь к исполняемому файлу оболочки readlink /proc/$$/exe
.
/proc
.
PS это самый надежный метод. Не гарантируется, что переменная окружения SHELL будет установлена, и даже если она есть, она может быть легко подделана.
У меня есть простой трюк, чтобы найти текущую оболочку. Просто введите случайную строку (которая не является командой). Он потерпит неудачу и вернет ошибку «not found», но в начале строки скажет, какая это оболочка:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
echo 'aaaa' > script; chmod +x script; ./script
дает./script.sh: 1: aaaa: not found
Далее всегда будет указана фактическая используемая оболочка - она получает имя фактического исполняемого файла, а не имя оболочки (т.е. ksh93
вместо ksh
и т. Д.). Для /bin/sh
, он покажет реальную оболочку используется, то есть dash
.
ls -l /proc/$$/exe | sed 's%.*/%%'
Я знаю, что многие говорят, что ls
вывод никогда не должен обрабатываться, но какова вероятность того, что у вас будет оболочка, которую вы используете, которая названа со специальными символами или помещена в каталог со специальными символами? Если это все еще так, есть много других примеров того, как сделать это по-другому.
Как указал Тоби Спейт , это было бы более правильным и более чистым способом достижения того же самого:
basename $(readlink /proc/$$/exe)
/proc
. Не все в мире Linux box.
basename $(readlink /proc/$$/exe)
чтобы ls
+ sed
+ echo
.
ash -> /bin/busybox
, это даст / bin / busybox.
Я перепробовал много разных подходов, и лучший для меня:
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.
ps
, tail
и gawk
, cmd не определяет $$
как его PID, поэтому он определенно не может работать под простым cmd.
ps -p$$ -o comm=
? POSIX говорит, что указание всех пустых заголовков полностью подавляет заголовок. Мы по-прежнему терпим неудачу (как и все ps
ответы), когда мы получаем непосредственно исполняемый скрипт (например #!/bin/sh
).
Мой вариант по распечатке родительского процесса:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
Не запускайте ненужные приложения, когда AWK может сделать это за вас.
awk
, когда ps -p "$$" -o 'comm='
можешь сделать это за тебя?
Существует много способов узнать оболочку и соответствующую ей версию. Вот несколько, которые работали для меня.
прямой
Хакерский подход
$> ******* (введите набор случайных символов, и в выходных данных вы получите имя оболочки. В моем случае -bash: chapter2-a-sample-isomorphic-app: команда не найдена )
При условии, что вы /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}'
-i
опции (-> ignore environment) env
в строке, где вы проверяете lsof
доступность. это терпит неудачу с: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
Если вы просто хотите проверить, что вы используете (конкретную версию) 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 будут совместимы.
Ни один из ответов не работал с 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. :(
sed: invalid option -- 'E'
на bash 3.2.51 и tcsh 6.15.00
Мое решение:
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).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
ненадежно. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
лучше, но почему бы просто не использовать ps -p $$
?
В Mac OS X (и FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
, и это дало мне -bash
.
mutt
...: -b
Подбирать PID из вывода «ps» не нужно, потому что вы можете прочитать соответствующую командную строку для любого PID из структуры каталогов / proc:
echo $(cat /proc/$$/cmdline)
Однако это может быть не лучше, чем просто:
echo $0
Что касается запуска фактически другой оболочки, чем указано в названии, одна идея состоит в том, чтобы запросить версию из оболочки, используя имя, которое вы получили ранее:
<some_shell> --version
sh
Кажется, что с ошибкой код выхода 2, в то время как другие дают что-то полезное (но я не могу проверить все, так как у меня их нет):
$ sh --version
sh: 0: Illegal option --
echo $?
2
Это не очень чистое решение, но оно делает то, что вы хотите.
# 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).
dash
, и yash
т.д. Как правило, если вы используете bash
, zsh
, ksh
, что угодно , - вы не должны заботиться о таких вещах.
ksh
набор функций - это в основном подмножество bash
функций (хотя я не проверил это полностью).
Чтобы узнать, использует ли ваша оболочка 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 и повторите попытку.
Пожалуйста, используйте следующую команду:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
делай то, что ты пытаешься делать, и делай это хорошо. Второй также не годится, потому что $SHELL
переменная окружения содержит оболочку по умолчанию для текущего пользователя, а не работающую в данный момент оболочку. Если я, например, bash
установил в качестве оболочки по умолчанию, выполнить zsh
и echo $SHELL
, он напечатает bash
.
echo $SHELL
может быть мусором: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Этот хорошо работает на Red Hat Linux (RHEL), macOS, BSD и некоторых AIX :
ps -T $$ | awk 'NR==2{print $NF}'
альтернативно, следующий также должен работать, если pstree доступен,
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
подстановка?), Вероятно, более переносимо, чем поиск имени оболочки. По местным обычаям вы можете запускать что-то по имени,/bin/sh
которое на самом деле может быть ash, dash, bash и т. Д.