Come posso determinare l'attuale shell su cui sto lavorando?
L'output del ps
comando da solo sarebbe sufficiente?
Come può essere fatto in diversi gusti di Unix?
Come posso determinare l'attuale shell su cui sto lavorando?
L'output del ps
comando da solo sarebbe sufficiente?
Come può essere fatto in diversi gusti di Unix?
Risposte:
Esistono tre approcci per trovare il nome dell'eseguibile della shell corrente:
Si noti che tutti e tre gli approcci possono essere ingannati se l'eseguibile della shell è /bin/sh
, ma è davvero un rinominato bash
, ad esempio (che accade spesso).
Quindi alla tua seconda domanda se l' ps
output farà sarà risposto con " non sempre ".
echo $0
- stamperà il nome del programma ... che nel caso della shell è la shell effettiva.
ps -ef | grep $$ | grep -v grep
- questo cercherà l'ID del processo corrente nell'elenco dei processi in esecuzione. Poiché il processo corrente è la shell, verrà incluso.
Questo non è affidabile al 100%, poiché potresti avere altri processi la cui ps
lista include lo stesso numero dell'ID processo della shell, specialmente se quell'ID è un numero piccolo (ad esempio, se il PID della shell è "5", potresti trovare processi chiamati "java5" o "perl5" nello stesso grep
output!). Questo è il secondo problema con l'approccio "ps", oltre a non poter fare affidamento sul nome della shell.
echo $SHELL
- Il percorso della shell corrente viene memorizzato come SHELL
variabile per qualsiasi shell. L'avvertenza per questo è che se avvii esplicitamente una shell come sottoprocesso (per esempio, non è la tua shell di login), otterrai invece il valore della tua shell di login. Se questa è una possibilità, usa l' approccio ps
o $0
.
Se, tuttavia, l'eseguibile non corrisponde alla shell reale (ad esempio, /bin/sh
è in realtà bash o ksh), è necessaria l'euristica. Ecco alcune variabili ambientali specifiche per varie shell:
$version
è impostato su tcsh
$BASH
è impostato su bash
$shell
(minuscolo) è impostato sul nome della shell effettivo in csh o tcsh
$ZSH_NAME
è impostato su zsh
ksh ha $PS3
e $PS4
imposta, mentre la normale shell Bourne ( sh
) ha solo $PS1
e $PS2
imposta. Questo sembra in generale come la più difficile da distinguere - l' unica differenza in tutta la serie di variabili d'ambiente tra il sh
e ksh
abbiamo installato su Solaris boxen è $ERRNO
, $FCEDIT
, $LINENO
, $PPID
, $PS3
, $PS4
, $RANDOM
, $SECONDS
, e $TMOUT
.
echo ${.sh.version}
restituisce "Sostituzione errata". Vedi la mia soluzione sopra
ps -ef | grep …
... Questo è affidabile al 100% come ...” Utilizzando una semplice espressione regolare via egrep
o grep -e
può facilmente portare l'affidabilità fino a per-tutte-intenti-e-fini 100%: ps -ef | egrep "^\s*\d+\s+$$\s+"
. Le ^
marche sicuro che siamo a partire dall'inizio della linea, le \d+
mangia l'UID, il $$
corrisponde il PID, e la \s*
e \s+
rappresentano e garantiscono spazi tra le altre parti.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
dovrebbe funzionare ovunque le soluzioni che coinvolgono ps -ef
e grep
fanno (su qualsiasi variante Unix che supporti le opzioni POSIX perps
) e non soffriranno dei falsi positivi introdotti dal grepping per una sequenza di cifre che possono apparire altrove.
ps
che potrebbe non essere comprensibile, -p
quindi potrebbe essere necessario utilizzarle /bin/ps -p $$
.
$$
tranne per le fish
quali dovresti usare ps -p %self
.
/bin/ps
. ps
potrebbe essere facilmente installato (attualmente è abbastanza normale al giorno d'oggi) /usr/bin
. $(which ps) -p $$
è un modo migliore. Naturalmente, questo non funzionerà nei pesci, e forse in alcuni altri gusci. Penso che sia (which ps) -p %self
nei pesci.
readlink /proc/$$/exe
sh
emulato da bash
, ps -p ti dà /usr/bin/bash
anche se lo esegui comesh
Provare
ps -p $$ -oargs=
o
ps -p $$ -ocomm=
ps -o fname --no-headers $$
.
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
. (La riga successiva nella mia sceneggiatura ha l'equivalente di csh.)
-q
invece di -p
:SHELL=$(ps -ocomm= -q $$)
Se vuoi solo assicurarti che l'utente stia invocando uno script con 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
. Con questa riga lo script viene eseguito usando bash anche se iniziato usando ksh o sh. Il mio caso d'uso non richiede argomenti da riga di comando, ma potrebbero essere aggiunti dopo, $0
se necessario.
Puoi provare:
ps | grep `echo $$` | awk '{ print $4 }'
O:
echo $SHELL
/pattern/ { action }
lo farà?
$SHELL
La variabile di ambiente contiene una shell, configurata come predefinita per un utente corrente. Non riflette una shell, che è attualmente in esecuzione. Inoltre è meglio usare ps -p $$
che fare grepping $$ a causa di falsi positivi.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
non è necessario mostrare sempre la shell corrente. Riflette solo la shell predefinita da invocare.
Per provare quanto sopra, dire che bash
è la shell predefinita, provare echo $SHELL
, quindi nello stesso terminale, entrare in qualche altra shell ( ad esempio KornShell (ksh)) e provare $SHELL
. Vedrai il risultato come bash in entrambi i casi.
Per ottenere il nome della shell corrente, utilizzare cat /proc/$$/cmdline
. E il percorso per la shell eseguibile da readlink /proc/$$/exe
.
/proc
.
ps è il metodo più affidabile. Non è possibile impostare la variabile d'ambiente SHELL e, anche se lo è, può essere facilmente falsificata.
Ho un semplice trucco per trovare la shell corrente. Basta digitare una stringa casuale (che non è un comando). Fallirà e restituirà un errore "non trovato", ma all'inizio della riga dirà quale shell è:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
echo 'aaaa' > script; chmod +x script; ./script
dà./script.sh: 1: aaaa: not found
Di seguito viene sempre fornita la shell effettiva utilizzata: ottiene il nome dell'eseguibile effettivo e non il nome della shell (ovvero ksh93
anziché ksh
, ecc.). Per /bin/sh
, mostrerà l'effettiva shell utilizzata, cioè dash
.
ls -l /proc/$$/exe | sed 's%.*/%%'
So che ci sono molti che affermano che l' ls
output non dovrebbe mai essere elaborato, ma qual è la probabilità che tu abbia una shell che stai usando che è nominata con caratteri speciali o collocata in una directory chiamata con caratteri speciali? Se questo è ancora il caso, ci sono molti altri esempi di farlo diversamente.
Come sottolineato da Toby Speight , questo sarebbe un modo più corretto e più pulito di ottenere lo stesso:
basename $(readlink /proc/$$/exe)
/proc
. Non tutto il mondo è una scatola Linux.
basename $(readlink /proc/$$/exe)
a ls
+ sed
+ echo
.
ash -> /bin/busybox
, questo darà / bin / busybox.
Ho provato molti approcci diversi e il migliore per me è:
ps -p $$
Funziona anche con Cygwin e non può produrre falsi positivi come grepping PID. Con un po 'di pulizia, genera solo un nome eseguibile (sotto Cygwin con percorso):
ps -p $$ | tail -1 | awk '{print $NF}'
Puoi creare una funzione in modo da non doverla memorizzare:
# Print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
... e poi esegui shell
.
È stato testato su Debian e Cygwin.
ps
, tail
e gawk
, cmd non definisce $$
come è PID, quindi sicuramente non può funzionare sotto il semplice cmd.
ps -p$$ -o comm=
? POSIX afferma che la specifica di tutte le intestazioni vuote elimina completamente l'intestazione. Stiamo ancora fallendo (come tutte le ps
risposte) quando provengono da uno script eseguito direttamente (ad es #!/bin/sh
.).
La mia variante sulla stampa del processo genitore:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
Non eseguire applicazioni non necessarie quando AWK può farlo per te.
awk
, quando ps -p "$$" -o 'comm='
puoi farlo per te?
Esistono molti modi per scoprire la shell e la sua versione corrispondente. Eccone alcuni che hanno funzionato per me.
schietto
Approccio Hackish
$> ******* (Digita un set di caratteri casuali e nell'output otterrai il nome della shell. Nel mio caso -bash: capitolo2-a-sample-isomorphic-app: comando non trovato )
A condizione che /bin/sh
supporti lo standard POSIX e che il tuo sistema abbia lsof
installato il comando - una possibile alternativa a questo lsof
potrebbe essere pid2path
- puoi anche usare (o adattare) il seguente script che stampa percorsi completi:
#!/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
dell'opzione (-> ignora ambiente) env
nella riga in cui controlli la lsof
disponibilità. fallisce con: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
Se vuoi solo verificare che stai eseguendo (una versione particolare di) Bash, il modo migliore per farlo è usare la $BASH_VERSINFO
variabile array. Come variabile di array (di sola lettura) non può essere impostata nell'ambiente, quindi puoi essere sicuro che provenga (se non del tutto) dalla shell corrente.
Tuttavia, poiché Bash ha un comportamento diverso quando viene invocato come sh
, è necessario verificare anche la $BASH
fine della variabile di ambiente /bash
.
In uno script che ho scritto che utilizza nomi di funzioni con -
(non trattino basso) e dipende da array associativi (aggiunti in Bash 4), ho il seguente controllo di integrità (con utile messaggio di errore dell'utente):
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
Potresti omettere il controllo funzionale un po 'paranoico per le funzionalità nel primo caso e supporre che le future versioni di Bash sarebbero compatibili.
Nessuna delle risposte ha funzionato con la fish
shell (non ha le variabili $$
o $0
).
Questo funziona per me (testato su sh
, bash
, fish
, ksh
, csh
, true
, tcsh
, e zsh
, openSUSE 13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
Questo comando genera una stringa come bash
. Qui sto solo usando ps
, tail
e sed
(senza estensioni GNU; prova ad aggiungere --posix
per verificarlo). Sono tutti comandi POSIX standard. Sono sicuro che tail
può essere rimosso, ma il mio sed
fu non è abbastanza forte per farlo.
Mi sembra che questa soluzione non sia molto portatile in quanto non funziona su OS X. :(
sed: invalid option -- 'E'
sulla bash 3.2.51 e tcsh 6.15.00
La mia soluzione:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
Questo dovrebbe essere portatile su diverse piattaforme e shell. Utilizza ps
come altre soluzioni, ma non fa affidamento su sed
o awk
e filtra la spazzatura dalle tubazioni e da ps
se stessa in modo che la shell sia sempre l'ultima voce. In questo modo non è necessario fare affidamento su variabili PID non portatili o selezionare le linee e le colonne giuste.
Ho testato su Debian e macOS con Bash, Z shell ( zsh
) e fish (che non funziona con la maggior parte di queste soluzioni senza cambiare l'espressione specificamente per i pesci, perché usa una variabile PID diversa).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
non è affidabile. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
è meglio, ma perché non usarlo ps -p $$
?
Su Mac OS X (e FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
Ho provato questo durante l'utilizzo e mi ha dato -bash
.
mutt
...: -b
Non è necessario il grepping PID dall'output di "ps", poiché è possibile leggere la rispettiva riga di comando per qualsiasi PID dalla struttura della directory / proc:
echo $(cat /proc/$$/cmdline)
Tuttavia, potrebbe non essere migliore del semplice:
echo $0
Informazioni sull'esecuzione di una shell effettivamente diversa da quella indicata dal nome, un'idea è richiedere la versione dalla shell utilizzando il nome ottenuto in precedenza:
<some_shell> --version
sh
sembra fallire con il codice di uscita 2 mentre altri danno qualcosa di utile (ma non sono in grado di verificare tutto poiché non li ho):
$ sh --version
sh: 0: Illegal option --
echo $?
2
Questa non è una soluzione molto pulita, ma fa quello che vuoi.
# 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
Adesso puoi usare $(getshell) --version
.
dash
, yash
ecc Normalmente, se si sta utilizzando bash
, zsh
, ksh
, qualunque sia - non si dovrebbe preoccuparsi di queste cose.
ksh
il set di funzionalità è per lo più un sottoinsieme delle bash
funzionalità (non ho controllato bene questo però).
Fai quanto segue per sapere se la tua shell sta usando Dash / Bash.
ls –la /bin/sh
:
se il risultato è /bin/sh -> /bin/bash
==> Quindi la tua shell sta usando Bash.
se il risultato è /bin/sh ->/bin/dash
==> Quindi la tua shell sta usando Dash.
Se vuoi passare da Bash a Dash o viceversa, usa il codice seguente:
ln -s /bin/bash /bin/sh
(cambia shell in Bash)
Nota : se il comando sopra riportato genera un errore che dice, / bin / sh esiste già, rimuovere / bin / sh e riprovare.
Si prega di utilizzare il comando seguente:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
fa quello che stai cercando di fare e fallo bene. Anche il secondo non va bene, perché $SHELL
la variabile di ambiente contiene una shell predefinita per un utente corrente, non una shell attualmente in esecuzione. Se ad esempio ho bash
impostato come shell predefinita, esegui zsh
e echo $SHELL
, verrà stampato bash
.
echo $SHELL
può essere spazzatura: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Questo funziona bene su Red Hat Linux (RHEL), macOS, BSD e alcuni AIX :
ps -T $$ | awk 'NR==2{print $NF}'
in alternativa, anche il seguente dovrebbe funzionare se è disponibile pstree ,
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
sostituzione?) È probabilmente più portabile che trovare il nome della shell. L'usanza locale potrebbe farti eseguire qualcosa di nome/bin/sh
che potrebbe effettivamente essere ash, dash, bash, ecc.