Come determinare la shell corrente su cui sto lavorando


634

Come posso determinare l'attuale shell su cui sto lavorando?

L'output del pscomando da solo sarebbe sufficiente?

Come può essere fatto in diversi gusti di Unix?


9
Testare capacità particolari (ad es. Fa la !sostituzione?) È probabilmente più portabile che trovare il nome della shell. L'usanza locale potrebbe farti eseguire qualcosa di nome /bin/shche potrebbe effettivamente essere ash, dash, bash, ecc.
msw

1
@msw: sembra un buon commento, tranne per il fatto che mi lascia chiedermi "come?".
nobar,

Sembra che non ci sia una risposta semplice a questa domanda. Se non possiamo interrogare la shell, forse l'approccio migliore è specificare sempre la shell. Non sono sicuro che ciò sia sempre possibile, ma forse è più facile da realizzare di quanto si pensi in genere.
nobar,


@Aniket, non tanto aiuto come potresti pensare - è interessato solo ai processi di shell interattivi .
Toby Speight,

Risposte:


794
  • 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' psoutput farà sarà risposto con " non sempre ".

    1. echo $0 - stamperà il nome del programma ... che nel caso della shell è la shell effettiva.

    2. 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 pslista 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 grepoutput!). Questo è il secondo problema con l'approccio "ps", oltre a non poter fare affidamento sul nome della shell.

    3. echo $SHELL- Il percorso della shell corrente viene memorizzato come SHELLvariabile 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 pso $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 $PS3e $PS4imposta, mentre la normale shell Bourne ( sh) ha solo $PS1e $PS2imposta. Questo sembra in generale come la più difficile da distinguere - l' unica differenza in tutta la serie di variabili d'ambiente tra il she kshabbiamo installato su Solaris boxen è $ERRNO, $FCEDIT, $LINENO, $PPID, $PS3, $PS4, $RANDOM, $SECONDS, e $TMOUT.


$ {. sh.version} è impostato su ksh93
fpmurphy il

14
ps -p $$come sottolinea Matthew Slattery . Per ksh: echo $KSH_VERSIONo echo ${.sh.version}.
In pausa fino a nuovo avviso.

@Dennish - il mio ksh in questo momento non ha KSH_VERSION impostato. e echo ${.sh.version}restituisce "Sostituzione errata". Vedi la mia soluzione sopra
DVK

3
ps -ef | grep …... Questo è affidabile al 100% come ...” Utilizzando una semplice espressione regolare via egrepo grep -epuò 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.
Slipp D. Thompson

2
@ SlippD.Thompson non ha funzionato in GNU / Linux. Ma questo sembra funzionare:ps -ef | awk '$2==pid' pid=$$
jarno

98

ps -p $$

dovrebbe funzionare ovunque le soluzioni che coinvolgono ps -efe grepfanno (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.


12
Alcune shell hanno la loro versione integrata psche potrebbe non essere comprensibile, -pquindi potrebbe essere necessario utilizzarle /bin/ps -p $$.
In pausa fino a nuovo avviso.

13
Tutte le conchiglie con cui ho familiarità comprendono, $$tranne per le fishquali dovresti usare ps -p %self.
In pausa fino a nuovo avviso.

3
In realtà, non dovresti fare affidamento su percorsi difficili come /bin/ps. pspotrebbe 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 %selfnei pesci.
Aron Cederholm,

1
In alcuni sistemi minimali come un contenitore docker debian-slim, ps potrebbe non esserci. In tal caso, questo approccio funziona ancora:readlink /proc/$$/exe
mxmlnkn,

se sei shemulato da bash, ps -p ti dà /usr/bin/bashanche se lo esegui comesh
Ding-Yi Chen il

45

Provare

ps -p $$ -oargs=

o

ps -p $$ -ocomm=

4
Questa è una bella breve. Mi stavo usando ps -o fname --no-headers $$.
Anne van Rossum,

Grazie. Ho trovato questa l'opzione migliore da usare in uno script per proteggere i comandi specifici di bash test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash. (La riga successiva nella mia sceneggiatura ha l'equivalente di csh.)
craq

1
Ho scoperto che se lo fai all'interno di una subshell, ciò può portare a righe extra spurie abbinando il PID del genitore e il processo di shell effettivo. Per questo, uso -qinvece di -p:SHELL=$(ps -ocomm= -q $$)
Steve,

35

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

1
Questo dovrebbe essere quello più vicino alla cima. Molte grazie.
Alex Skrypnyk,

4
Non dovrebbe essere più vicino all'inizio, perché non risponde affatto alla domanda. Se la domanda fosse "Come verificare se lo script è in esecuzione sotto bash", voto per questo.
David Ferenczy Rogožan,

2
@DawidFerenczy - Questa domanda è il risultato migliore quando cerchi quella frase. Nel lungo periodo, penso che sia molto più importante che le risposte rispondano a ciò che la gente sta cercando piuttosto che rispondere alla domanda originale.
ArtOfWarfare il

3
Inoltre, la variabile $ BASH viene definita in tcsh se la tcsh viene invocata da bash
18446744073709551615

Questo è molto utile, grazie. Solo adattato un po ', mettendo questo come seconda riga dopo #!/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, $0se necessario.
joanis,

20

Puoi provare:

ps | grep `echo $$` | awk '{ print $4 }'

O:

echo $SHELL

6
Qual è il punto di grep seguito da awk, quando /pattern/ { action }lo farà?
Jens,

# in zshell alias shell = 'echo $ {SHELL: t}'
SergioAraujo

4
$SHELLLa 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.
David Ferenczy Rogožan,

L'env $ SHELL. la variabile punta alla shell 'parent', come specificato nelle specifiche POSIX: SHELL Questa variabile deve rappresentare un percorso dell'interprete del linguaggio di comando preferito dall'utente. Pertanto, il valore di $ SHELL potrebbe non essere la shell corrente.
Theo,

tutti dentro awk,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
go2null,

16

$SHELLnon è 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.


8
... Se hai /proc.
Tripleee

10

ps è il metodo più affidabile. Non è possibile impostare la variabile d'ambiente SHELL e, anche se lo è, può essere facilmente falsificata.


12
+1 $ SHELL è la shell predefinita per i programmi che devono generarne una. Non riflette necessariamente la shell attualmente in esecuzione.
Jim Lewis,

8

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

3
Non va bene in una sceneggiatura. echo 'aaaa' > script; chmod +x script; ./script./script.sh: 1: aaaa: not found
slim

6

Di seguito viene sempre fornita la shell effettiva utilizzata: ottiene il nome dell'eseguibile effettivo e non il nome della shell (ovvero ksh93anziché 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' lsoutput 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)

3
Questo solo errori su tutti gli Unices che non forniscono /proc. Non tutto il mondo è una scatola Linux.
Jens,

5
E se siete su Linux, preferiremmo basename $(readlink /proc/$$/exe)a ls+ sed+ echo.
Toby Speight,

1
Questo darà il nome del reale eseguibile , non il vero e proprio guscio . Quando la shell reale è collegata come applet busybox, diciamo ash -> /bin/busybox, questo darà / bin / busybox.
stepse

6

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.


Dal mio setup (Cygwin | Windows 7) la tua risposta è la migliore, e ps -p $$ | coda -1 | gawk '{print $ NF}' funziona anche da cmd senza $ bash. Nota gawk invece di awk, poiché awk funziona solo da bash.
WebComer

@WebComer Spiacente, non sono sicuro di cosa intendi con " funziona anche da cmd senza $ bash ". Anche se avresti le porte Windows di ps, taile gawk, cmd non definisce $$come è PID, quindi sicuramente non può funzionare sotto il semplice cmd.
David Ferenczy Rogožan,

Perché non semplicemente ps -p$$ -o comm=? POSIX afferma che la specifica di tutte le intestazioni vuote elimina completamente l'intestazione. Stiamo ancora fallendo (come tutte le psrisposte) quando provengono da uno script eseguito direttamente (ad es #!/bin/sh.).
Toby Speight,

5

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.


2
Perché eseguire un inutile awk, quando ps -p "$$" -o 'comm='puoi farlo per te?
Toby Speight,

5

Esistono molti modi per scoprire la shell e la sua versione corrispondente. Eccone alcuni che hanno funzionato per me.

schietto

  1. $> echo $ 0 (Ti dà il nome del programma. Nel mio caso l'output era -bash .)
  2. $> $ SHELL (Questo ti porta nella shell e nel prompt ottieni il nome e la versione della shell. Nel mio caso bash3.2 $ .)
  3. $> echo $ SHELL (Questo ti darà il percorso eseguibile. Nel mio caso / bin / bash .)
  4. $> $ SHELL --version (Questo fornirà informazioni complete sul software shell con tipo di licenza)

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 )


3

A condizione che /bin/shsupporti lo standard POSIX e che il tuo sistema abbia lsofinstallato il comando - una possibile alternativa a questo lsofpotrebbe 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}'

1
questo funziona nei pesci! ma per me non riesce a bash, a causa -idell'opzione (-> ignora ambiente) envnella riga in cui controlli la lsofdisponibilità. fallisce con: env -i PATH="${PATH}" type lsof->env: ‘type’: No such file or directory
hoijui

2

Se vuoi solo verificare che stai eseguendo (una versione particolare di) Bash, il modo migliore per farlo è usare la $BASH_VERSINFOvariabile 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 $BASHfine 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.


2

Nessuna delle risposte ha funzionato con la fishshell (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, taile sed(senza estensioni GNU; prova ad aggiungere --posixper verificarlo). Sono tutti comandi POSIX standard. Sono sicuro che tailpuò essere rimosso, ma il mio sedfu non è abbastanza forte per farlo.

Mi sembra che questa soluzione non sia molto portatile in quanto non funziona su OS X. :(


Ottengo sed: invalid option -- 'E'sulla bash 3.2.51 e tcsh 6.15.00
craq

2

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 pscome altre soluzioni, ma non fa affidamento su sedo awke filtra la spazzatura dalle tubazioni e da psse 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).



1

Su Mac OS X (e FreeBSD):

ps -p $$ -axco command | sed -n '$p' 

2
zshHo provato questo durante l'utilizzo e mi ha dato -bash.
user137369

Sul mio sistema (ora), testato con bash e trattino, questo ritorno mutt...: -b
F. Hauri

1

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

1

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.

Funziona, tuttavia, solo su shell simili a KornShell (ksh).


1
"Questo funziona, però, solo su shell tipo ksh." Stai dicendo che devo verificare la shell prima di eseguire questo? Hmm ...
jpaugh

@jpaugh, le liste devono essere supportati dalla shell di sourcing questo codice, che non è il caso di dash, yashecc Normalmente, se si sta utilizzando bash, zsh, ksh, qualunque sia - non si dovrebbe preoccuparsi di queste cose.
theoden8,

Quindi conti bash come "ksh-like"? Fatto. Questo ha più senso
jpaugh il

@jpaugh, beh, questo è ciò che intendevo perché kshil set di funzionalità è per lo più un sottoinsieme delle bashfunzionalità (non ho controllato bene questo però).
theoden8,

1

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.


0

E ho pensato a questo

sed 's/.*SHELL=//; s/[[:upper:]].*//' /proc/$$/environ

Funzionerà solo su Linux ! Non MacOS, né BSD !!
F. Hauri,

-1

Si prega di utilizzare il comando seguente:

ps -p $$ | tail -1 | awk '{print $4}'

Il primo è una sciocchezza, echo $SHELLfa quello che stai cercando di fare e fallo bene. Anche il secondo non va bene, perché $SHELLla variabile di ambiente contiene una shell predefinita per un utente corrente, non una shell attualmente in esecuzione. Se ad esempio ho bashimpostato come shell predefinita, esegui zshe echo $SHELL, verrà stampato bash.
David Ferenczy Rogožan,

Hai ragione Dawid Ferenczy, possiamo usare il comando ps per determinare la shell corrente. [# ps -p $$ | coda -1 | awk '{print $ 4}'].
Ranjithkumar T,

echo $SHELLpuò essere spazzatura: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
SaltwaterC

-2

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}'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.