Jak mogę ustalić bieżącą powłokę, nad którą pracuję?
Czy wynik samego ps
polecenia byłby wystarczający?
Jak można to zrobić w różnych wersjach Uniksa?
Jak mogę ustalić bieżącą powłokę, nad którą pracuję?
Czy wynik samego ps
polecenia byłby wystarczający?
Jak można to zrobić w różnych wersjach Uniksa?
Odpowiedzi:
Istnieją trzy podejścia do znalezienia nazwy pliku wykonywalnego bieżącej powłoki:
Zauważ, że wszystkie trzy podejścia można oszukać, jeśli plik wykonywalny powłoki jest /bin/sh
, ale tak naprawdę bash
na przykład zmienia się jego nazwę (co często się zdarza).
Zatem na twoje drugie pytanie, czy ps
wynik da wynik, odpowiada „ nie zawsze ”.
echo $0
- wypisze nazwę programu ... która w przypadku powłoki jest rzeczywistą powłoką.
ps -ef | grep $$ | grep -v grep
- wyszuka bieżący identyfikator procesu na liście uruchomionych procesów. Ponieważ bieżącym procesem jest powłoka, zostanie on uwzględniony.
Nie jest to w 100% niezawodne, ponieważ możesz mieć inne procesy, których ps
lista zawiera taki sam numer jak identyfikator procesu powłoki, szczególnie jeśli ten identyfikator jest małą liczbą (na przykład, jeśli PID powłoki to „5”, możesz znaleźć procesy o nazwie „java5” lub „perl5” na tym samym grep
wyjściu!). Jest to drugi problem związany z podejściem „ps”, poza tym, że nie można polegać na nazwie powłoki.
echo $SHELL
- Ścieżka do bieżącej powłoki jest przechowywana jako SHELL
zmienna dla dowolnej powłoki. Zastrzeżeniem tego jest to, że jeśli uruchomisz powłokę jawnie jako podproces (na przykład nie jest to twoja powłoka logowania), zamiast tego otrzymasz wartość powłoki logowania. Jeśli jest to możliwe, użyj metody ps
lub $0
.
Jeśli jednak plik wykonywalny nie pasuje do twojej aktualnej powłoki (np. /bin/sh
Jest to właściwie bash lub ksh), potrzebujesz heurystyki. Oto niektóre zmienne środowiskowe specyficzne dla różnych powłok:
$version
jest ustawiony na tcsh
$BASH
jest ustawiony na bash
$shell
(małe litery) ustawiono na rzeczywistą nazwę powłoki w csh lub tcsh
$ZSH_NAME
jest ustawiony na zsh
ksh ma $PS3
i $PS4
ustawia, podczas gdy normalna powłoka Bourne'a ( sh
) ma $PS1
i $PS2
ustawia. To wydaje się jak zwykle najtrudniejsze do odróżnienia - z jedyną różnicę w całym zbiorze zmiennych środowiskowych pomiędzy sh
i ksh
mamy zainstalowany na Solaris Boxen jest $ERRNO
, $FCEDIT
, $LINENO
, $PPID
, $PS3
, $PS4
, $RANDOM
, $SECONDS
, i $TMOUT
.
echo ${.sh.version}
zwraca „Złe zastąpienie”. Zobacz moje rozwiązanie powyżej
ps -ef | grep …
... To nie jest w 100% wiarygodne jako ...” Za pomocą prostego wyrażenia regularnego poprzez egrep
lub grep -e
może łatwo doprowadzić do wiarygodności dla-all-zamiarów-and-celów w 100%: ps -ef | egrep "^\s*\d+\s+$$\s+"
. W ^
pilnuje, zaczynamy od początku linii, \d+
zjada UID, urządzenie $$
pasuje PID, a \s*
i \s+
konto dla & zapewnić odstępy między innymi częściami.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
powinien działać wszędzie tam, gdzie występują rozwiązania ps -ef
i grep
zrobić (na dowolnym wariancie Unix, który obsługuje opcje dla POSIXps
) i nie będzie cierpieć z powodu fałszywych alarmów wprowadzonych grepping na sekwencję cyfr, które mogą pojawić się w innym miejscu.
ps
których może nie zrozumieć, -p
więc może być konieczne użycie /bin/ps -p $$
.
$$
wyjątkiem fish
których będziesz musiał użyć ps -p %self
.
/bin/ps
. ps
można łatwo zainstalować (obecnie jest to całkiem normalne) /usr/bin
. $(which ps) -p $$
to lepszy sposób. Oczywiście nie zadziała to u ryb i prawdopodobnie innych skorup. Myślę, że jest (which ps) -p %self
w rybach.
readlink /proc/$$/exe
sh
jest naśladowany bash
, ps -p daje ci /usr/bin/bash
nawet uruchomić go jakosh
Próbować
ps -p $$ -oargs=
lub
ps -p $$ -ocomm=
ps -o fname --no-headers $$
.
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
. (Następny wiersz w moim skrypcie ma odpowiednik csh.)
-q
zamiast -p
:SHELL=$(ps -ocomm= -q $$)
Jeśli chcesz tylko upewnić się, że użytkownik wywołuje skrypt za pomocą 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
. W tym wierszu skrypt jest uruchamiany przy użyciu bash, nawet jeśli zaczął używać ksh lub sh. Mój przypadek użycia nie wymaga argumentów z wiersza poleceń, ale można je dodać po nim, $0
jeśli to konieczne.
Możesz spróbować:
ps | grep `echo $$` | awk '{ print $4 }'
Lub:
echo $SHELL
/pattern/ { action }
to zrobi?
$SHELL
zmienna środowiskowa zawiera powłokę, która jest domyślnie skonfigurowana dla bieżącego użytkownika. Nie odzwierciedla powłoki, która jest obecnie uruchomiona. Lepiej jest także używać ps -p $$
niż grepping $$ z powodu fałszywych wyników pozytywnych.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
nie zawsze musi pokazywać bieżącą powłokę. Odzwierciedla tylko domyślną powłokę, którą należy wywołać.
Aby przetestować powyższe, powiedzmy, że bash
jest domyślną powłoką, spróbuj echo $SHELL
, a następnie w tym samym terminalu, przejdź do innej powłoki ( na przykład KornShell (ksh)) i spróbuj $SHELL
. W obu przypadkach zobaczysz wynik jako bash.
Aby uzyskać nazwę bieżącej powłoki, użyj cat /proc/$$/cmdline
. I ścieżka do powłoki wykonywalnej przez readlink /proc/$$/exe
.
/proc
.
ps jest najbardziej niezawodną metodą. Nie można zagwarantować, że zmienna środowiskowa SHELL zostanie ustawiona, a nawet jeśli tak, można ją łatwo sfałszować.
Mam prostą sztuczkę, aby znaleźć bieżącą powłokę. Wystarczy wpisać losowy ciąg znaków (który nie jest poleceniem). Nie powiedzie się i zwróci błąd „nie znaleziono”, ale na początku linii powie, która to powłoka:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
echo 'aaaa' > script; chmod +x script; ./script
daje./script.sh: 1: aaaa: not found
Poniższe zawsze podaje rzeczywistą używaną powłokę - pobiera nazwę rzeczywistego pliku wykonywalnego, a nie nazwę powłoki (tj. ksh93
Zamiast ksh
itp.). Bo /bin/sh
pokaże rzeczywistą używaną powłokę, tj dash
.
ls -l /proc/$$/exe | sed 's%.*/%%'
Wiem, że jest wielu, którzy mówią ls
wyjściowe nigdy nie powinny być przetwarzane, ale jakie jest prawdopodobieństwo, że będziesz mieć używaną powłokę nazwaną znakami specjalnymi lub umieszczoną w katalogu o nazwach specjalnych? Jeśli nadal tak jest, istnieje wiele innych przykładów robienia tego inaczej.
Jak zauważył Toby Speight , byłby to bardziej właściwy i czystszy sposób osiągnięcia tego samego:
basename $(readlink /proc/$$/exe)
/proc
. Nie cały świat jest systemem Linux.
basename $(readlink /proc/$$/exe)
do ls
+ sed
+ echo
.
ash -> /bin/busybox
, to da / bin / busybox.
Próbowałem wielu różnych podejść i najlepsze dla mnie jest:
ps -p $$
Działa również pod Cygwinem i nie może generować fałszywych alarmów jako grepping PID. Po pewnym czyszczeniu wyświetla tylko nazwę pliku wykonywalnego (pod Cygwin ze ścieżką):
ps -p $$ | tail -1 | awk '{print $NF}'
Możesz stworzyć funkcję, żebyś nie musiał jej zapamiętywać:
# Print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
... a następnie po prostu wykonaj shell
.
Został przetestowany pod Debianem i Cygwinem.
ps
, tail
a gawk
cmd nie definiuje się $$
jako PID, więc zdecydowanie nie może działać pod zwykłym cmd.
ps -p$$ -o comm=
? POSIX mówi, że określenie wszystkich nagłówków pustych całkowicie pomija nagłówek. Nadal nie udaje nam się (podobnie jak wszystkie ps
odpowiedzi), gdy pozyskujemy bezpośrednio wykonany skrypt (np #!/bin/sh
.).
Mój wariant drukowania procesu nadrzędnego:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
Nie uruchamiaj niepotrzebnych aplikacji, gdy AWK może to zrobić za Ciebie.
awk
, kiedy ps -p "$$" -o 'comm='
można to dla Ciebie zrobić?
Istnieje wiele sposobów na znalezienie powłoki i odpowiadającej jej wersji. Oto kilka, które działały dla mnie.
Bezpośredni
Podstępne podejście
$> ******* (Wpisz zestaw losowych znaków, a na wyjściu otrzymasz nazwę powłoki. W moim przypadku -bash: rozdział2-a-sample-isomorphic-app: polecenie nie znaleziono )
Pod warunkiem, że /bin/sh
obsługujesz standard POSIX, a system ma lsof
zainstalowaną komendę - lsof
w tym przypadku może być alternatywą pid2path
- możesz także użyć (lub dostosować) następującego skryptu, który wypisuje pełne ścieżki:
#!/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
w linii, w której sprawdzasz lsof
dostępność. kończy się niepowodzeniem: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
Jeśli chcesz tylko sprawdzić, czy korzystasz (konkretna wersja) Bash, najlepszym sposobem na to jest użycie $BASH_VERSINFO
zmiennej tablicowej. Jako zmienna tablicowa (tylko do odczytu) nie można jej ustawić w środowisku, więc możesz być pewien, że pochodzi ona (jeśli w ogóle) z bieżącej powłoki.
Ponieważ jednak Bash ma inne zachowanie po wywołaniu jako sh
, musisz również sprawdzić, czy $BASH
zmienna środowiskowa kończy się na /bash
.
W skrypcie, który napisałem, który używa nazw funkcji z -
(bez podkreślenia) i zależy od tablic asocjacyjnych (dodanych w Bash 4), mam następującą kontrolę poprawności (z pomocnym komunikatem o błędzie użytkownika):
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
W pierwszym przypadku można pominąć nieco paranoiczne sprawdzenie działania funkcji i założyć, że przyszłe wersje Bash będą kompatybilne.
Żadna z odpowiedzi nie działała z fish
powłoką (nie ma zmiennych $$
lub $0
).
Działa to dla mnie (testowane na sh
, bash
, fish
, ksh
, csh
, true
, tcsh
, i zsh
; openSUSE 13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
To polecenie wyświetla ciąg podobny do bash
. Tutaj jestem tylko przy użyciu ps
, tail
oraz sed
(bez extesions GNU, spróbuj dodać --posix
to sprawdzić). Wszystkie są standardowymi poleceniami POSIX. Jestem pewien, że tail
można go usunąć, ale moje sed
fu nie jest wystarczająco silne, aby to zrobić.
Wydaje mi się, że to rozwiązanie nie jest zbyt przenośne, ponieważ nie działa w systemie OS X. :(
sed: invalid option -- 'E'
bash 3.2.51 i tcsh 6.15.00
Moje rozwiązanie:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
Powinno to być przenośne na różnych platformach i powłokach. Wykorzystuje ps
jak inne rozwiązania, ale to nie zależy sed
czy awk
i odfiltrowuje śmieci z orurowaniem i ps
sobie tak, że powłoka powinna być zawsze ostatni wpis. W ten sposób nie musimy polegać na nieprzenośnych zmiennych PID ani wybierać odpowiednich linii i kolumn.
Testowałem na Debianie i macOS z powłoką Bash, Z (zsh
) i fish (który nie działa z większością tych rozwiązań bez zmiany wyrażenia specjalnie dla ryb, ponieważ używa innej zmiennej PID).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
jest zawodne. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
jest lepszy, ale dlaczego nie po prostu użyć ps -p $$
?
W systemie Mac OS X (i FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
i dało mi to -bash
.
mutt
...: -b
Grepping PID z wyjścia „ps” nie jest potrzebny, ponieważ można odczytać odpowiedni wiersz polecenia dla dowolnego PID ze struktury katalogów / proc:
echo $(cat /proc/$$/cmdline)
Jednak nie może to być nic lepszego niż po prostu:
echo $0
Aby uruchomić właściwie inną powłokę niż wskazuje nazwa, jednym z pomysłów jest zażądanie wersji z powłoki przy użyciu wcześniej otrzymanej nazwy:
<some_shell> --version
sh
wydaje się nie działać z kodem wyjścia 2, podczas gdy inni dają coś pożytecznego (ale nie jestem w stanie zweryfikować wszystkiego, ponieważ ich nie mam):
$ sh --version
sh: 0: Illegal option --
echo $?
2
To nie jest bardzo czyste rozwiązanie, ale robi to, co chcesz.
# 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
Teraz możesz użyć $(getshell) --version
.
Działa to jednak tylko w przypadku powłok podobnych do KornShell (ksh).
dash
, yash
etc. Zwykle, jeśli używasz bash
, zsh
, ksh
, cokolwiek - nie należy dbać o takich rzeczach.
ksh
zestaw funkcji jest w większości podzbiorem bash
funkcji (chociaż nie sprawdziłem tego dokładnie).
Wykonaj następujące czynności, aby wiedzieć, czy twoja powłoka używa Dash / Bash.
ls –la /bin/sh
:
jeśli wynikiem jest /bin/sh -> /bin/bash
==> Twoja powłoka używa Bash.
jeśli wynikiem jest /bin/sh ->/bin/dash
==> Twoja powłoka używa Dash.
Jeśli chcesz zmienić Bash na Dash lub odwrotnie, użyj poniższego kodu:
ln -s /bin/bash /bin/sh
(zmień powłokę na Bash)
Uwaga : Jeśli powyższe polecenie powoduje błąd: / bin / sh już istnieje, usuń / bin / sh i spróbuj ponownie.
Prosimy użyć następującego polecenia:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
robi to, co próbujesz zrobić i robi to dobrze. Drugi też nie jest dobry, ponieważ $SHELL
zmienna środowiskowa zawiera domyślną powłokę dla bieżącego użytkownika, a nie aktualnie działającą powłokę. Jeśli mam na przykład bash
Ustaw jako domyślny shell, wykonanie zsh
i echo $SHELL
będziesz drukować bash
.
echo $SHELL
mogą być śmieci: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Ten działa dobrze na Red Hat Linux (RHEL), macOS, BSD i niektórych systemach AIX :
ps -T $$ | awk 'NR==2{print $NF}'
alternatywnie, poniższy powinien również działać, jeśli pstree jest dostępny,
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
podstawienia?) Jest prawdopodobnie bardziej przenośne niż znajdowanie nazwy powłoki. Lokalny zwyczaj może wymagać, abyś uruchomił coś o nazwie,/bin/sh
która może być popiołem, myślnikiem, uderzeniem itp.