Як визначити поточну оболонку, над якою працюю


632

Як я можу визначити поточну оболонку, над якою працюю?

Чи psвистачить лише результату команди?

Як це можна зробити в різних ароматах Unix?


9
Тестування конкретних можливостей (наприклад, чи робиться це !підміна?), Ймовірно, більш портативний, ніж пошук назви оболонки. Місцевий звичай, можливо, у вас працює щось з ім'ям, /bin/shяке насправді може бути попелом, тире,
башком

1
@msw: Здається, хороший коментар, за винятком того, що я не замислююся над тим, як "."
nobar

Здається, що на це питання немає простої відповіді. Якщо ми не можемо запитувати оболонку, можливо, кращим підходом є завжди вказати оболонку. Я не впевнений, що це завжди можливо, але, можливо, це зробити легше, ніж люди взагалі припускають.
nobar

Це може допомогти -> opensourceforgeeks.blogspot.in/2013/05/…
Aniket Thakur

@Aniket, не стільки допомога, скільки можна подумати - це цікавить лише інтерактивні процеси оболонки.
Toby Speight

Відповіді:


793
  • Існує три підходи до пошуку імені виконавчого файлу поточної оболонки:

    Зверніть увагу, що всі три підходи можна обдурити, якщо виконуваний файл оболонки є /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 встановлюється на bash

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

    • $ZSH_NAME встановлюється на zsh

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


$ {. sh.version} встановлено на ksh93
fpmurphy

14
ps -p $$як вказує Метью Сллатери . Для ksh: echo $KSH_VERSIONабо echo ${.sh.version}.
Призупинено до подальшого повідомлення.

@Dennish - у моєму ksh зараз не встановлено KSH_VERSION. і echo ${.sh.version}повертає "Погана заміна". Дивіться моє рішення вище
DVK

3
« ps -ef | grep …... Це не 100% надійні , як ...» З допомогою простого регулярного виразу з допомогою egrepабо grep -eможна легко довести надійність до для-всіх-намірів-і-цілі 100%: ps -ef | egrep "^\s*\d+\s+$$\s+". У ^переконуюся ми починаємо з початку лінії, \d+з'їдає UID, то $$збігається з PID, а \s*й \s+рахунки для забезпечення і пропусків між іншими частинами.
Сліп Д. Томпсон

2
@ SlippD.Thompson не працював у GNU / Linux. Але це, здається, працює:ps -ef | awk '$2==pid' pid=$$
jarno

98

ps -p $$

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


12
Деякі оболонки мають власну вбудовану версію, psяка може не розуміти, -pтому вам може знадобитися використовувати /bin/ps -p $$.
Призупинено до подальшого повідомлення.

12
Усі снаряди, з якими я добре знайомий, $$крім тих, fishз якими вам доведеться користуватися ps -p %self.
Призупинено до подальшого повідомлення.

3
Насправді, ви не повинні покладатися на важкі шляхи, такі як /bin/ps . psможна було легко (фактично це цілком нормально в наш час) встановити в /usr/bin. $(which ps) -p $$є кращим способом. Звичайно, це не спрацює з рибою, а можливо, і з іншими черепашками. Я думаю, що це (which ps) -p %selfв рибі.
Арон Кедерхолм

1
У деяких мінімальних системах, таких як debian-тонкий докер-контейнер, 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необхідності.
joanis

20

Ви можете спробувати:

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

Або:

echo $SHELL

6
У чому сенс грепу, за яким слідує awk, коли /pattern/ { action }зробимо?
Єнс

# in zshell alias shell = 'echo $ {SHELL: t}'
SergioAraujo

4
$SHELLзмінна середовище містить оболонку, яка налаштована як типова для поточного користувача. Він не відображає оболонку, яка зараз працює. Крім того, краще використовувати, ps -p $$ніж прибирати $$ через помилкові позитиви.
Девід Ференчі Рогожан

$ 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 . Ви побачите результат як баш в обох випадках.

Щоб отримати ім'я поточної оболонки, Використовуйте cat /proc/$$/cmdline. І шлях до оболонки, виконуваної readlink /proc/$$/exe.


8
... За умови, що у вас є /proc.
tripleee

10

ps - найнадійніший метод. Змінна середовища SHELL не встановлена, і навіть якщо вона є, вона може бути легко підроблена.


12
+1 $ SHELL - оболонка за замовчуванням для програм, які потребують нерестування. Це не обов'язково відображає оболонку, яка працює в даний час.
Джим Льюїс

8

У мене є простий трюк, щоб знайти поточну оболонку. Просто введіть випадковий рядок (який не є командою). Він вийде з ладу і поверне помилку "не знайдено", але на початку рядка скаже, що це оболонка:

ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found

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)

3
Це просто помилки в усіх Unices, які не надаються /proc. Не у всьому світі вікно Linux.
Єнс

5
І якщо ви працюєте в Linux, ми вважаємо basename $(readlink /proc/$$/exe)за краще ls+ sed+ echo.
Toby Speight

1
Це дасть назву фактичного виконуваного файлу , а не фактичної оболонки . Коли фактична оболонка пов'язана як аплет зайнятого, скажімо ash -> /bin/busybox, це дасть / bin / busybox.
крок

6

Я спробував багато різних підходів, і найкращий для мене такий:

ps -p $$

Він також працює під Cygwin і не може видавати помилкові позитиви як PID-зібрання. Після деякого очищення, він видає просто ім'я, яке виконується (під 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).
Toby Speight

5

Мій варіант друку батьківського процесу:

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

Не запускайте зайві програми, коли AWK може це зробити за вас.


2
Навіщо запускати зайве awk, коли це ps -p "$$" -o 'comm='можна зробити за вас?
Toby Speight

5

Існує багато способів дізнатися оболонку та відповідну її версію. Ось декілька, які працювали на мене.

Прямо

  1. $> echo $ 0 (дає вам назву програми. У моєму випадку результат був -bash .)
  2. $> $ SHELL (Це вводить вас у оболонку, і у відповідь ви отримаєте ім'я та версію оболонки. У моєму випадку bash3.2 $ .)
  3. $> echo $ SHELL (Це дасть вам виконавчий шлях. У моєму випадку / bin / bash .)
  4. $> $ SHELL - перехід (Це дасть повну інформацію про програмне забезпечення оболонки з типом ліцензії)

Хекістський підхід

$> ******* (Введіть набір випадкових символів, і у висновку ви отримаєте ім'я оболонки. У моєму випадку -bash: Chapter2-a-sample-izomorphic-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, найкращий спосіб зробити це - використовувати $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; спробуйте додати, --posixщоб перевірити це). Всі вони є стандартними командами POSIX. Я впевнена, що tailїї можна видалити, але мій sedфу недостатньо сильний для цього.

Мені здається, що це рішення не дуже портативне, оскільки не працює в ОС X. :(


Я отримую 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 (який не працює з більшістю цих рішень, не змінюючи вираження спеціально для риб, оскільки він використовує іншу змінну PID).


1
echo $$ # Gives the Parent Process ID 
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.

Звідки ви знаєте, яка ваша поточна оболонка? .


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' 

2
Спробував це під час використання zsh, і це дало мені -bash.
користувач137369

У моїй системі (зараз), протестованої на баш та тире, це повернення mutt...: -b
F. Hauri

1

Збирання PID з виводу "ps" не потрібно, оскільки ви можете прочитати відповідний командний рядок для будь-якого PID зі структури каталогу / proc:

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

Однак це може бути не краще, ніж просто:

echo $0

Щодо запуску фактично іншої оболонки, ніж вказує ім’я, одна ідея полягає у тому, щоб запитувати версію з оболонки, використовуючи раніше ім’я:

<some_shell> --version

sh здається, не виходить з кодом виходу 2, тоді як інші дають щось корисне (але я не в змозі перевірити все, оскільки у мене їх немає):

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

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 (кш).


1
"Хоча це працює, лише на оболонках, схожих на ksh". Ви кажете, що я повинен перевірити оболонку, перш ніж це запустити? Хм ...
jpaugh

@jpaugh, списки повинні бути підтримані оболонки Sourcing цей код, який не відноситься до dash, і yashт.д. Як правило, якщо ви використовуєте bash, zsh, ksh, що завгодно , - ви не повинні піклуватися про такі речі.
theoden8

Отже, ви вважаєте баш "кш-подібним"? Зрозумів. Це має більше сенсу
jpaugh

@jpaugh, ну, ось що я мав на увазі, тому kshщо набір функцій - це, як правило, підмножина bashфункцій (хоча це ще не перевірено ретельно).
theoden8

1

Виконайте наступне, щоб знати, чи використовує ваша оболонка Dash / Bash.

ls –la /bin/sh:

  • якщо результат /bin/sh -> /bin/bash==> Тоді ваша оболонка використовує Bash.

  • якщо результат /bin/sh ->/bin/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}'].
Ranjithkumar T

echo $SHELLможе бути сміттям: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Соленої

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.