Wie kann ich die aktuelle Shell bestimmen, an der ich arbeite?
Wäre die Ausgabe des ps
Befehls allein ausreichend?
Wie kann dies in verschiedenen Unix-Varianten erfolgen?
Wie kann ich die aktuelle Shell bestimmen, an der ich arbeite?
Wäre die Ausgabe des ps
Befehls allein ausreichend?
Wie kann dies in verschiedenen Unix-Varianten erfolgen?
Antworten:
Es gibt drei Ansätze, um den Namen der ausführbaren Datei der aktuellen Shell zu ermitteln:
Bitte beachten Sie, dass alle drei Ansätze getäuscht werden können, wenn die ausführbare Datei der Shell ist /bin/sh
, aber es ist bash
zum Beispiel wirklich eine Umbenennung (was häufig vorkommt).
Daher wird Ihre zweite Frage, ob die ps
Ausgabe funktioniert, mit " nicht immer " beantwortet .
echo $0
- gibt den Programmnamen aus ... der im Fall der Shell die eigentliche Shell ist.
ps -ef | grep $$ | grep -v grep
- Hiermit wird nach der aktuellen Prozess-ID in der Liste der ausgeführten Prozesse gesucht. Da der aktuelle Prozess die Shell ist, wird sie eingeschlossen.
Dies ist nicht 100% zuverlässig, da Sie möglicherweise andere Prozesse haben, deren ps
Auflistung dieselbe Nummer wie die Prozess-ID der Shell enthält, insbesondere wenn diese ID eine kleine Zahl ist (wenn die PID der Shell beispielsweise "5" ist, werden möglicherweise Prozesse aufgerufen "java5" oder "perl5" in derselben grep
Ausgabe!). Dies ist das zweite Problem beim "ps" -Ansatz, da man sich nicht auf den Shell-Namen verlassen kann.
echo $SHELL
- Der Pfad zur aktuellen Shell wird als SHELL
Variable für jede Shell gespeichert . Die Einschränkung für diesen Fall ist, dass Sie stattdessen den Wert Ihrer Anmeldeshell erhalten, wenn Sie eine Shell explizit als Unterprozess starten (z. B. nicht Ihre Anmeldeshell). Wenn dies möglich ist, verwenden Sie den Ansatz ps
oder $0
.
Wenn die ausführbare Datei jedoch nicht mit Ihrer tatsächlichen Shell übereinstimmt (z. B. /bin/sh
bash oder ksh), benötigen Sie Heuristiken. Hier sind einige Umgebungsvariablen, die für verschiedene Schalen spezifisch sind:
$version
ist auf tcsh eingestellt
$BASH
ist auf Bash gesetzt
$shell
(Kleinbuchstaben) wird in csh oder tcsh auf den tatsächlichen Shell-Namen gesetzt
$ZSH_NAME
ist auf zsh gesetzt
ksh hat $PS3
und $PS4
gesetzt, während die normale Bourne-Shell ( sh
) nur hat $PS1
und $PS2
setzt. Dies scheint im Allgemeinen wie die am schwersten zu unterscheiden - den einzigen Unterschied in der gesamten Reihe von Umgebungsvariablen zwischen sh
und ksh
wir haben auf Solaris boxt installiert ist $ERRNO
, $FCEDIT
, $LINENO
, $PPID
, $PS3
, $PS4
, $RANDOM
, $SECONDS
, und $TMOUT
.
echo ${.sh.version}
gibt "Bad Substitution" zurück. Siehe meine Lösung oben
ps -ef | grep …
... Dies ist nicht 100% zuverlässig als ..." Die Verwendung eines einfachen regulären Ausdrucks über egrep
oder grep -e
kann die Zuverlässigkeit auf 100% für alle Zwecke und Zwecke erhöhen : ps -ef | egrep "^\s*\d+\s+$$\s+"
. Das ^
stellt sicher, dass wir am Anfang der Zeile beginnen, dass die \d+
UID aufgefressen wird, dass $$
die PID übereinstimmt und dass \s*
und \s+
Leerzeichen zwischen den anderen Teilen berücksichtigt und sichergestellt werden.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
sollte überall funktionieren, wo die Lösungen involviert sind ps -ef
und grep
funktionieren (bei jeder Unix-Variante, die POSIX-Optionen für unterstütztps
) und nicht unter den Fehlalarmen leiden, die durch das Suchen nach einer Folge von Ziffern entstehen, die an anderer Stelle erscheinen können.
ps
die möglicherweise nicht verstanden wird, -p
sodass Sie sie möglicherweise verwenden müssen /bin/ps -p $$
.
$$
außer denen, fish
mit denen Sie sie verwenden müssten ps -p %self
.
/bin/ps
. ps
könnte leicht (eigentlich ist es heutzutage ganz normal) eingebaut werden /usr/bin
. $(which ps) -p $$
ist ein besserer Weg. Natürlich funktioniert dies nicht bei Fischen und möglicherweise einigen anderen Muscheln. Ich denke, es ist (which ps) -p %self
in Fisch.
readlink /proc/$$/exe
sh
emuliert wird bash
, geben ps -p Ihnen /usr/bin/bash
sogar, dass Sie es alssh
Versuchen
ps -p $$ -oargs=
oder
ps -p $$ -ocomm=
ps -o fname --no-headers $$
.
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
. (Die nächste Zeile in meinem Skript hat das Äquivalent für csh.)
-q
anstelle von -p
:SHELL=$(ps -ocomm= -q $$)
Wenn Sie nur sicherstellen möchten, dass der Benutzer ein Skript mit Bash aufruft:
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
. Mit dieser Zeile wird das Skript mit bash ausgeführt, auch wenn es mit ksh oder sh gestartet wurde. Mein Anwendungsfall benötigt keine Befehlszeilenargumente, kann aber $0
bei Bedarf nachträglich hinzugefügt werden.
Du kannst es versuchen:
ps | grep `echo $$` | awk '{ print $4 }'
Oder:
echo $SHELL
/pattern/ { action }
wird es gehen?
$SHELL
Die Umgebungsvariable enthält eine Shell, die für einen aktuellen Benutzer als Standard konfiguriert ist. Es spiegelt keine Shell wider, die gerade ausgeführt wird. Es ist auch besser zu verwenden, ps -p $$
als $$ wegen falsch positiver Ergebnisse zu greppen.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
muss nicht immer die aktuelle Shell anzeigen. Es gibt nur die Standard-Shell wieder, die aufgerufen werden soll.
Um das oben Gesagte zu testen, sagen wir, es bash
ist die Standard-Shell, versuchen Sie es echo $SHELL
und rufen Sie dann im selben Terminal eine andere Shell auf ( z. B. KornShell (ksh)) und versuchen Sie es $SHELL
. In beiden Fällen wird das Ergebnis als Bash angezeigt.
Verwenden Sie Verwenden, um den Namen der aktuellen Shell abzurufen cat /proc/$$/cmdline
. Und der Pfad zur ausführbaren Shell von readlink /proc/$$/exe
.
/proc
.
ps ist die zuverlässigste Methode. Es ist nicht garantiert, dass die Umgebungsvariable SHELL festgelegt wird, und selbst wenn dies der Fall ist, kann sie leicht gefälscht werden.
Ich habe einen einfachen Trick, um die aktuelle Shell zu finden. Geben Sie einfach eine zufällige Zeichenfolge ein (was kein Befehl ist). Es wird fehlschlagen und einen "nicht gefundenen" Fehler zurückgeben, aber am Anfang der Zeile wird angezeigt, um welche Shell es sich handelt:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
echo 'aaaa' > script; chmod +x script; ./script
gibt./script.sh: 1: aaaa: not found
Im Folgenden wird immer die tatsächlich verwendete Shell angegeben - es wird der Name der tatsächlich ausführbaren Datei und nicht der Shell-Name (dh ksh93
anstelle von ksh
usw.) abgerufen. Denn /bin/sh
es wird die tatsächlich verwendete Shell angezeigt, dh dash
.
ls -l /proc/$$/exe | sed 's%.*/%%'
Ich weiß, dass es viele gibt, die sagen, dass die ls
Ausgabe niemals verarbeitet werden sollte, aber wie hoch ist die Wahrscheinlichkeit, dass Sie eine Shell verwenden, die mit Sonderzeichen benannt oder in einem Verzeichnis mit Sonderzeichen abgelegt ist? Wenn dies immer noch der Fall ist, gibt es viele andere Beispiele dafür, wie man es anders macht.
Wie Toby Speight hervorhob , wäre dies ein angemessener und sauberer Weg, um dasselbe zu erreichen:
basename $(readlink /proc/$$/exe)
/proc
. Nicht die ganze Welt ist eine Linux-Box.
basename $(readlink /proc/$$/exe)
zu ls
+ sed
+ echo
.
ash -> /bin/busybox
, wird dies / bin / Busybox ergeben.
Ich habe viele verschiedene Ansätze ausprobiert und der beste für mich ist:
ps -p $$
Es funktioniert auch unter Cygwin und kann keine falsch positiven Ergebnisse als PID-Grepping erzeugen. Bei einigen Bereinigungen wird nur ein ausführbarer Name ausgegeben (unter Cygwin mit Pfad):
ps -p $$ | tail -1 | awk '{print $NF}'
Sie können eine Funktion erstellen, damit Sie sie sich nicht merken müssen:
# Print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
... und dann einfach ausführen shell
.
Es wurde unter Debian und Cygwin getestet.
ps
, tail
und gawk
, cmd wird nicht definiert , $$
wie PID es ist , so dass es auf jeden Fall kann es nicht Arbeit unter der Ebene cmd.
ps -p$$ -o comm=
? POSIX sagt, dass die Angabe aller leeren Header den Header vollständig unterdrückt. Wir scheitern immer noch (wie alle ps
Antworten), wenn wir von einem direkt ausgeführten Skript (z #!/bin/sh
. B. ) bezogen werden.
Meine Variante zum Drucken des übergeordneten Prozesses:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
Führen Sie keine unnötigen Anwendungen aus, wenn AWK dies für Sie tun kann.
awk
, wann ps -p "$$" -o 'comm='
kann es für Sie tun?
Es gibt viele Möglichkeiten, die Shell und ihre entsprechende Version herauszufinden. Hier sind einige, die für mich gearbeitet haben.
Einfach
Hackischer Ansatz
$> ******* ( Geben Sie eine Reihe von zufälligen Zeichen ein und in der Ausgabe erhalten Sie den Shell-Namen. In meinem Fall -bash: Kapitel2-a-sample-isomorphic-app: Befehl nicht gefunden )
Vorausgesetzt, Sie /bin/sh
unterstützen den POSIX-Standard und auf Ihrem System ist der lsof
Befehl installiert - eine mögliche Alternative zu lsof
diesem Fall pid2path
-, können Sie auch das folgende Skript verwenden (oder anpassen), das vollständige Pfade druckt:
#!/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
Option (-> Umgebung ignorieren) env
in der Zeile fehl , in der Sie prüfen lsof
, ob sie verfügbar ist. es schlägt fehl mit: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
Wenn Sie nur überprüfen möchten, ob Sie (eine bestimmte Version von) Bash ausführen, verwenden Sie am besten die $BASH_VERSINFO
Array-Variable. Als (schreibgeschützte) Array-Variable kann sie nicht in der Umgebung festgelegt werden, sodass Sie sicher sein können, dass sie (wenn überhaupt) von der aktuellen Shell stammt.
Da Bash beim Aufrufen als ein anderes Verhalten aufweist sh
, müssen Sie auch überprüfen, ob die $BASH
Umgebungsvariable mit endet /bash
.
In einem Skript, das ich geschrieben habe und das Funktionsnamen mit -
(nicht Unterstrich) verwendet und von assoziativen Arrays abhängt (hinzugefügt in Bash 4), habe ich die folgende Überprüfung der Integrität (mit hilfreicher Benutzerfehlermeldung):
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
Sie könnten im ersten Fall die etwas paranoide Funktionsprüfung für Funktionen weglassen und einfach davon ausgehen, dass zukünftige Bash-Versionen kompatibel sind.
Keine der Antworten funktionierte mit fish
Shell (es hat nicht die Variablen $$
oder $0
).
Dies funktioniert für mich (getestet auf sh
, bash
, fish
, ksh
, csh
, true
, tcsh
, und zsh
; openSUSE 13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
Dieser Befehl gibt eine Zeichenfolge wie aus bash
. Hier bin ich nur mit ps
, tail
und sed
(ohne GNU extesions, versuchen Sie fügen --posix
es zu überprüfen). Dies sind alles Standard-POSIX-Befehle. Ich bin sicher, tail
kann entfernt werden, aber mein sed
Fu ist nicht stark genug, um dies zu tun.
Es scheint mir, dass diese Lösung nicht sehr portabel ist, da sie unter OS X nicht funktioniert. :(
sed: invalid option -- 'E'
auf Bash 3.2.51 und tcsh 6.15.00
Meine Lösung:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
Dies sollte auf verschiedenen Plattformen und Shells portierbar sein. Es wird ps
wie andere Lösungen verwendet, ist jedoch nicht auf sed
oder angewiesen awk
und filtert Müll aus den Rohrleitungen und sich ps
selbst heraus, sodass die Shell immer der letzte Eintrag sein sollte. Auf diese Weise müssen wir uns nicht auf nicht tragbare PID-Variablen verlassen oder die richtigen Zeilen und Spalten auswählen.
Ich habe unter Debian und macOS mit Bash, Z shell ( zsh
) und fish getestet (was mit den meisten dieser Lösungen nicht funktioniert, ohne den Ausdruck speziell für fish zu ändern, da eine andere PID-Variable verwendet wird).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
ist unzuverlässig. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
ist besser, aber warum nicht einfach verwenden ps -p $$
?
Unter Mac OS X (und FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
, und es gab mir -bash
.
mutt
...: -b
Das Abrufen der PID von der Ausgabe von "ps" ist nicht erforderlich, da Sie die entsprechende Befehlszeile für jede PID aus der Verzeichnisstruktur / proc lesen können:
echo $(cat /proc/$$/cmdline)
Das ist jedoch vielleicht nicht besser als nur:
echo $0
Wenn Sie eine andere Shell ausführen möchten, als der Name angibt, sollten Sie die Version unter dem zuvor angegebenen Namen von der Shell anfordern:
<some_shell> --version
sh
scheint mit Exit-Code 2 zu scheitern, während andere etwas Nützliches geben (aber ich kann nicht alle überprüfen, da ich sie nicht habe):
$ sh --version
sh: 0: Illegal option --
echo $?
2
Dies ist keine sehr saubere Lösung, aber sie macht, was Sie wollen.
# 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
Jetzt können Sie verwenden $(getshell) --version
.
Dies funktioniert jedoch nur bei KornShell- ähnlichen Shells (ksh).
dash
, yash
usw. Normalerweise , wenn Sie mit bash
, zsh
, ksh
, was auch immer - Sie sollten über solche Dinge nicht.
ksh
die Funktionen meistens eine Teilmenge der bash
Funktionen sind (habe dies jedoch nicht gründlich überprüft).
Gehen Sie folgendermaßen vor, um festzustellen, ob Ihre Shell Dash / Bash verwendet.
ls –la /bin/sh
::
Wenn das Ergebnis /bin/sh -> /bin/bash
==> ist, verwendet Ihre Shell Bash.
Wenn das Ergebnis /bin/sh ->/bin/dash
==> ist, verwendet Ihre Shell Dash.
Wenn Sie von Bash zu Dash oder umgekehrt wechseln möchten, verwenden Sie den folgenden Code:
ln -s /bin/bash /bin/sh
(Shell in Bash ändern)
Hinweis : Wenn der obige Befehl zu einem Fehler führt, der besagt, dass / bin / sh bereits vorhanden ist, entfernen Sie / bin / sh und versuchen Sie es erneut.
Bitte benutzen Sie den folgenden Befehl:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
macht das , was Sie versuchen, und macht es gut. Die zweite ist ebenfalls nicht gut, da die $SHELL
Umgebungsvariable die Standard-Shell für einen aktuellen Benutzer enthält, keine aktuell ausgeführte Shell. Wenn ich zum Beispiel bash
als Standard-Shell festgelegt habe, führen Sie zsh
und aus echo $SHELL
, es wird gedruckt bash
.
echo $SHELL
kann Müll sein: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Dieser funktioniert gut unter Red Hat Linux (RHEL), MacOS, BSD und einigen AIXs :
ps -T $$ | awk 'NR==2{print $NF}'
alternativ sollte das folgende auch funktionieren, wenn pstree verfügbar ist:
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
ersetzt?) Ist wahrscheinlich portabler als das Finden des Namens der Shell. Lokale Gewohnheit könnte Sie etwas ausführen lassen,/bin/sh
das tatsächlich Asche, Bindestrich, Bash usw. sein könnte