Як я можу визначити поточну оболонку, над якою працюю?
Чи 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яке насправді може бути попелом, тире,