Як я можу визначити поточну оболонку, над якою працюю?
Чи 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
встановлюється на 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
.
echo ${.sh.version}
повертає "Погана заміна". Дивіться моє рішення вище
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 $$
ніж прибирати $$ через помилкові позитиви.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
не завжди потрібно показувати поточну оболонку. Він відображає лише оболонку за замовчуванням, яку слід викликати.
Щоб перевірити вищесказане, скажіть, bash
це оболонка за замовчуванням, спробуйте echo $SHELL
, а потім у тому ж терміналі, перейдіть до іншої оболонки (наприклад, KornShell (ksh)) і спробуйте$SHELL
. Ви побачите результат як баш в обох випадках.
Щоб отримати ім'я поточної оболонки, Використовуйте cat /proc/$$/cmdline
. І шлях до оболонки, виконуваної readlink /proc/$$/exe
.
/proc
.
ps - найнадійніший метод. Змінна середовища SHELL не встановлена, і навіть якщо вона є, вона може бути легко підроблена.
У мене є простий трюк, щоб знайти поточну оболонку. Просто введіть випадковий рядок (який не є командою). Він вийде з ладу і поверне помилку "не знайдено", але на початку рядка скаже, що це оболонка:
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.
basename $(readlink /proc/$$/exe)
за краще ls
+ sed
+ echo
.
ash -> /bin/busybox
, це дасть / bin / busybox.
Я спробував багато різних підходів, і найкращий для мене такий:
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.
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-izomorphic-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, найкращий спосіб зробити це - використовувати $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; спробуйте додати, --posix
щоб перевірити це). Всі вони є стандартними командами POSIX. Я впевнена, що tail
її можна видалити, але мій sed
фу недостатньо сильний для цього.
Мені здається, що це рішення не дуже портативне, оскільки не працює в ОС 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 (який не працює з більшістю цих рішень, не змінюючи вираження спеціально для риб, оскільки він використовує іншу змінну 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 (кш).
dash
, і yash
т.д. Як правило, якщо ви використовуєте bash
, zsh
, ksh
, що завгодно , - ви не повинні піклуватися про такі речі.
ksh
що набір функцій - це, як правило, підмножина bash
функцій (хоча це ще не перевірено ретельно).
Виконайте наступне, щоб знати, чи використовує ваша оболонка 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 і повторіть спробу.
Будь ласка, використовуйте команду нижче:
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
яке насправді може бути попелом, тире,