Как определить текущую оболочку, над которой я работаю?
Будет ли выход 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 и т. Д.