Comment déterminer le shell actuel sur lequel je travaille


634

Comment puis-je déterminer le shell actuel sur lequel je travaille?

La sortie de la pscommande suffirait-elle à elle seule?

Comment cela peut-il être fait dans différentes versions d'Unix?


9
Tester des capacités particulières (par exemple, fait-il de la !substitution?) Est probablement plus portable que de trouver le nom du shell. La coutume locale pourrait vous faire exécuter quelque chose nommé /bin/shqui pourrait en fait être ash, dash, bash, etc.
msw

1
@msw: On dirait un bon commentaire sauf qu'il me laisse me demander "comment?".
nobar

Il semble qu'il n'y ait pas de réponse simple à cette question. Si nous ne pouvons pas interroger le shell, la meilleure approche est peut-être de toujours spécifier le shell. Je ne suis pas sûr que ce soit toujours possible, mais c'est peut-être plus facile à réaliser que les gens ne le pensent généralement.
nobar


@Aniket, pas autant d'aide qu'on pourrait le penser - qui ne s'intéresse qu'aux processus shell interactifs .
Toby Speight

Réponses:


794
  • Il existe trois approches pour trouver le nom de l'exécutable du shell actuel:

    Veuillez noter que les trois approches peuvent être dupées si l'exécutable du shell l'est /bin/sh, mais c'est vraiment un renommé bash, par exemple (ce qui arrive fréquemment).

    Ainsi, votre deuxième question de savoir si la pssortie fera l'affaire est répondue par " pas toujours ".

    1. echo $0 - affichera le nom du programme ... qui dans le cas du shell est le shell réel.

    2. ps -ef | grep $$ | grep -v grep- cela recherchera l'ID de processus actuel dans la liste des processus en cours d'exécution. Puisque le processus actuel est le shell, il sera inclus.

      Ce n'est pas fiable à 100%, car vous pourriez avoir d' autres processus dont la psliste comprend le même numéro que l'ID de processus du shell, en particulier si cet ID est un petit nombre (par exemple, si le PID du shell est "5", vous pouvez trouver des processus appelés "java5" ou "perl5" dans la même grepsortie!). C'est le deuxième problème avec l'approche "ps", en plus de ne pas pouvoir s'appuyer sur le nom du shell.

    3. echo $SHELL- Le chemin vers le shell courant est stocké comme SHELLvariable pour n'importe quel shell. La mise en garde pour celui-ci est que si vous lancez un shell explicitement en tant que sous-processus (par exemple, ce n'est pas votre shell de connexion), vous obtiendrez la valeur de votre shell de connexion à la place. Si c'est une possibilité, utilisez l' approche psou $0.


  • Si, cependant, l'exécutable ne correspond pas à votre shell réel (par exemple, /bin/shest en fait bash ou ksh), vous avez besoin d'une heuristique. Voici quelques variables environnementales spécifiques à différents coques:

    • $version est défini sur tcsh

    • $BASH est réglé sur bash

    • $shell (en minuscules) est défini sur le nom réel du shell dans csh ou tcsh

    • $ZSH_NAME est défini sur zsh

    • ksh a $PS3et $PS4mis, tandis que le shell Bourne normal ( sh) n'a que $PS1et $PS2mis. Cela semble généralement comme les plus difficiles à distinguer - la seule différence dans l'ensemble des variables d'environnement entre shet kshnous avons installé sur Solaris Boxen est $ERRNO, $FCEDIT, $LINENO, $PPID, $PS3, $PS4, $RANDOM, $SECONDSet $TMOUT.


$ {. sh.version} est défini sur ksh93
fpmurphy

14
ps -p $$comme le souligne Matthew Slattery . Pour ksh: echo $KSH_VERSIONou echo ${.sh.version}.
pause jusqu'à nouvel ordre.

@Dennish - mon ksh en ce moment n'a pas KSH_VERSION défini. et echo ${.sh.version}renvoie "Bad Substitution". Voir ma solution ci
DVK

3
« ps -ef | grep …... Ce n'est pas fiable à 100% que ... » En utilisant une expression régulière simple via egrepou grep -epeut apporter facilement la fiabilité jusqu'à pour-tout-et-intentions fins 100%: ps -ef | egrep "^\s*\d+\s+$$\s+". Les ^marques que nous commençons dès le début de la ligne, les \d+Eats l'UID, le $$correspond au PID, et \s*et \s+compte et assurer des espaces entre les autres parties.
Slipp D. Thompson

2
@ SlippD.Thompson ne fonctionnait pas sous GNU / Linux. Mais cela semble fonctionner:ps -ef | awk '$2==pid' pid=$$
jarno

98

ps -p $$

devrait fonctionner partout où les solutions impliquent ps -efet grepfont (sur toute variante Unix qui prend en charge les options POSIXps ) et ne souffrira pas des faux positifs introduits par la recherche d'une séquence de chiffres qui peut apparaître ailleurs.


12
Certains shells ont leur propre version intégrée psqui peut ne pas comprendre -p, vous devrez donc peut-être l'utiliser /bin/ps -p $$.
pause jusqu'à nouvel ordre.

13
Tous les obus que je connais comprennent, $$sauf ceux fishavec lesquels vous auriez à utiliser ps -p %self.
pause jusqu'à nouvel ordre.

3
En fait, vous ne devriez pas compter sur des chemins difficiles tels que /bin/ps. pspourrait facilement (en fait c'est tout à fait normal de nos jours) être installé dans /usr/bin. $(which ps) -p $$est une meilleure façon. Bien sûr, cela ne fonctionnera pas dans les poissons, et éventuellement dans d'autres coquilles. Je pense que c'est (which ps) -p %selfdans le poisson.
Aron Cederholm

1
Dans certains systèmes minimaux comme un docker debian-slim, ps peut ne pas être là. Dans ce cas, cette approche fonctionne toujours:readlink /proc/$$/exe
mxmlnkn

si votre shest émulé par bash, ps -p vous donne /usr/bin/bashmême si vous l'exécutez commesh
Ding-Yi Chen

45

Essayer

ps -p $$ -oargs=

ou

ps -p $$ -ocomm=

4
Ceci est un joli court. Je m'utilisais ps -o fname --no-headers $$.
Anne van Rossum

Merci. J'ai trouvé que c'était la meilleure option à utiliser dans un script pour protéger des commandes spécifiques à bash test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash. (La ligne suivante de mon script a l'équivalent de csh.)
craq

1
J'ai trouvé que si vous faites cela à partir d'un sous-shell, cela peut conduire à des lignes supplémentaires parasites en faisant correspondre le PID du parent ainsi que le processus de shell réel. Pour cela, j'utilise à la -qplace de -p:SHELL=$(ps -ocomm= -q $$)
Steve

35

Si vous voulez simplement vous assurer que l'utilisateur invoque un script avec Bash:

if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi

1
Ce devrait être celui le plus proche du sommet. Merci beaucoup.
Alex Skrypnyk

4
Il ne devrait pas être plus proche du sommet, car il ne répond pas du tout à la question. Si la question est "Comment vérifier si le script s'exécute sous bash", je vote pour.
David Ferenczy Rogožan

2
@DawidFerenczy - Cette question est le meilleur résultat lorsque vous recherchez cette phrase. À long terme, je pense qu'il est beaucoup plus important que les réponses répondent à ce que les gens recherchent plutôt que de répondre à la question d'origine.
ArtOfWarfare

3
De plus, la variable $ BASH est définie sous tcsh si le tcsh est
appelé

C'est très utile, merci. Juste adapté un peu, en mettant cela comme la deuxième ligne après #!/bin/bash: if [ ! -n "$BASH" ] ;then exec bash $0; fi. Avec cette ligne, le script est exécuté à l'aide de bash même s'il a commencé à utiliser ksh ou sh. Mon cas d'utilisation n'a pas besoin d'arguments de ligne de commande, mais ils pourraient être ajoutés après $0si nécessaire.
joanis

20

Tu peux essayer:

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

Ou:

echo $SHELL

6
Quel est l'intérêt de grep suivi de awk, quand /pattern/ { action }va faire?
Jens

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

4
$SHELLLa variable d'environnement contient un shell, qui est configuré par défaut pour un utilisateur actuel. Il ne reflète pas un shell en cours d'exécution. Il est également préférable d'utiliser ps -p $$que de saluer $$ en raison de faux positifs.
David Ferenczy Rogožan

Le $ SHELL env. La variable pointe vers le shell «parent», comme spécifié dans la spécification POSIX: SHELL Cette variable doit représenter un chemin d'accès de l'interpréteur de langage de commande préféré de l'utilisateur. Par conséquent, la valeur de $ SHELL peut ne pas être le shell actuel.
Theo

tous à l'intérieur awk,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
go2null

16

$SHELLn'a pas toujours besoin d'afficher le shell actuel. Il ne reflète que le shell par défaut à invoquer.

Pour tester ce qui précède, disons bashest le shell par défaut, essayez echo $SHELL, puis dans le même terminal, entrez dans un autre shell ( KornShell (ksh) par exemple) et essayez $SHELL. Vous verrez le résultat comme bash dans les deux cas.

Pour obtenir le nom du shell actuel, utilisez cat /proc/$$/cmdline. Et le chemin vers le shell exécutable par readlink /proc/$$/exe.


8
... à condition que vous l'ayez /proc.
tripleee

10

ps est la méthode la plus fiable. La variable d'environnement SHELL n'est pas garantie d'être définie et même si elle l'est, elle peut être facilement usurpée.


12
+1 $ SHELL est le shell par défaut pour les programmes qui doivent en générer un. Il ne reflète pas nécessairement le shell en cours d'exécution.
Jim Lewis

8

J'ai une astuce simple pour trouver le shell actuel. Tapez simplement une chaîne aléatoire (qui n'est pas une commande). Il échouera et renverra une erreur "introuvable", mais au début de la ligne, il dira de quel shell il s'agit:

ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found

3
Pas bon dans un script. echo 'aaaa' > script; chmod +x script; ./scriptdonne./script.sh: 1: aaaa: not found
slim

6

Ce qui suit donnera toujours le shell réel utilisé - il obtient le nom de l'exécutable réel et non le nom du shell (c'est- ksh93à- dire au lieu de ksh, etc.). Pour /bin/sh, il montrera le shell réellement utilisé, ie dash.

ls -l /proc/$$/exe | sed 's%.*/%%'

Je sais qu'il y en a beaucoup qui disent que la lssortie ne devrait jamais être traitée, mais quelle est la probabilité que vous ayez un shell que vous utilisez qui est nommé avec des caractères spéciaux ou placé dans un répertoire nommé avec des caractères spéciaux? Si tel est toujours le cas, il existe de nombreux autres exemples pour le faire différemment.

Comme l'a souligné Toby Speight , ce serait une façon plus appropriée et plus propre d'y parvenir:

basename $(readlink /proc/$$/exe)

3
Cela ne fait que des erreurs sur tous les Unices qui ne fournissent pas /proc. Tout le monde n'est pas une boîte Linux.
Jens

5
Et si vous êtes sous Linux, nous préférerions basename $(readlink /proc/$$/exe)à ls+ sed+ echo.
Toby Speight

1
Cela donnera le nom de l' exécutable réel , pas le shell réel . Lorsque le shell réel est lié en tant qu'applet busybox, disons ash -> /bin/busybox, cela donnera / bin / busybox.
stepse

6

J'ai essayé de nombreuses approches différentes et la meilleure pour moi est:

ps -p $$

Il fonctionne également sous Cygwin et ne peut pas produire de faux positifs comme greffe PID. Avec un peu de nettoyage, il sort juste un nom exécutable (sous Cygwin avec chemin):

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

Vous pouvez créer une fonction pour ne pas avoir à la mémoriser:

# Print currently active shell
shell () {
  ps -p $$ | tail -1 | awk '{print $NF}'
}

... puis exécutez shell.

Il a été testé sous Debian et Cygwin.


Depuis ma configuration (Cygwin | Windows 7), votre réponse est la meilleure, et ps -p $$ | queue -1 | gawk '{print $ NF}' fonctionne même à partir de cmd sans $ bash. Veuillez noter gawk au lieu de awk, car awk ne fonctionne que depuis bash.
WebComer

@WebComer Désolé, je ne suis pas sûr de ce que vous entendez par " fonctionne même à partir de cmd sans $ bash ". Même si vous avez des ports Windows ps, tailet gawk, cmd ne se définit pas $$comme étant un PID, il ne peut donc certainement pas fonctionner sous la cmd standard.
David Ferenczy Rogožan

Pourquoi pas simplement ps -p$$ -o comm=? POSIX indique que la spécification de tous les en-têtes vides supprime complètement l'en-tête. Nous échouons toujours (comme toutes les psréponses) lorsque nous sommes sourcés par un script exécuté directement (par exemple #!/bin/sh).
Toby Speight

5

Ma variante sur l'impression du processus parent:

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

N'exécutez pas d'applications inutiles quand AWK peut le faire pour vous.


2
Pourquoi exécuter un inutile awk, quand ps -p "$$" -o 'comm='peut-il le faire pour vous?
Toby Speight

5

Il existe de nombreuses façons de découvrir le shell et sa version correspondante. Voici quelques-uns qui ont fonctionné pour moi.

Simple

  1. $> echo $ 0 (vous donne le nom du programme. Dans mon cas, la sortie était -bash .)
  2. $> $ SHELL (Cela vous emmène dans le shell et à l'invite vous obtenez le nom et la version du shell. Dans mon cas bash3.2 $ .)
  3. $> echo $ SHELL (Cela vous donnera un chemin exécutable. Dans mon cas / bin / bash .)
  4. $> $ SHELL --version (Cela donnera des informations complètes sur le logiciel shell avec le type de licence)

Approche hackish

$> ******* (Tapez un ensemble de caractères aléatoires et dans la sortie, vous obtiendrez le nom du shell. Dans mon cas -bash: chapter2-a-sample-isomorphic-app: command not found )


3

À condition que votre /bin/shsupporte la norme POSIX et que votre système ait la lsofcommande installée - une alternative possible à lsofpourrait dans ce cas être pid2path- vous pouvez également utiliser (ou adapter) le script suivant qui imprime les chemins complets:

#!/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
cela fonctionne dans les poissons! mais pour moi, échoue dans bash, en raison de l' -ioption (-> ignorer l'environnement) envdans la ligne où vous vérifiez lsofsa disponibilité. il échoue avec: env -i PATH="${PATH}" type lsof->env: ‘type’: No such file or directory
hoijui

2

Si vous voulez simplement vérifier que vous exécutez (une version particulière de) Bash, la meilleure façon de le faire est d'utiliser la $BASH_VERSINFOvariable de tableau. En tant que variable de tableau (en lecture seule), elle ne peut pas être définie dans l'environnement, vous pouvez donc être sûr qu'elle provient (le cas échéant) du shell actuel.

Cependant, puisque Bash a un comportement différent lorsqu'il est appelé as sh, vous devez également vérifier la $BASHfin de la variable d'environnement avec /bash.

Dans un script que j'ai écrit qui utilise des noms de fonction avec -(pas de soulignement), et dépend des tableaux associatifs (ajoutés dans Bash 4), j'ai le contrôle de cohérence suivant (avec un message d'erreur utilisateur utile):

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

Vous pouvez omettre la vérification fonctionnelle quelque peu paranoïaque des fonctionnalités dans le premier cas, et supposer simplement que les futures versions de Bash seront compatibles.


2

Aucune des réponses n'a fonctionné avec le fishshell (il n'a pas les variables $$ou $0).

Cela fonctionne pour moi (testé sur sh, bash, fish, ksh, csh, true, tcsh, et zsh, openSUSE 13.2):

ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'

Cette commande génère une chaîne comme bash. Ici , je suis seulement en utilisant ps, tailet sed(sans extesions GNU, essayez d'ajouter --posixà vérifier). Ce sont toutes des commandes POSIX standard. Je suis sûr qu'il tailpeut être retiré, mais mon sedfu n'est pas assez fort pour le faire.

Il me semble que cette solution n'est pas très portable car elle ne fonctionne pas sous OS X. :(


Je reçois sed: invalid option -- 'E'sur bash 3.2.51 et tcsh 6.15.00
craq

2

Ma solution:

ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1

Cela devrait être portable sur différentes plates-formes et coquilles. Il utilise pscomme les autres solutions, mais il ne s'appuie pas sur sedou awket filtre les fichiers indésirables de la tuyauterie et de pslui - même, de sorte que le shell doit toujours être la dernière entrée. De cette façon, nous n'avons pas besoin de compter sur des variables PID non portables ou de choisir les bonnes lignes et colonnes.

J'ai testé sur Debian et macOS avec Bash, Z shell ( zsh) et fish (ce qui ne fonctionne pas avec la plupart de ces solutions sans changer l'expression spécifiquement pour fish, car il utilise une variable PID différente).



1

Sous Mac OS X (et FreeBSD):

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

2
J'ai essayé cela en utilisant zsh, et ça m'a donné -bash.
user137369

Sur mon système (maintenant), testé avec bash et dash, ce retour mutt...: -b
F. Hauri

1

Grepping PID de la sortie de "ps" n'est pas nécessaire, car vous pouvez lire la ligne de commande respective pour n'importe quel PID à partir de la structure du répertoire / proc:

echo $(cat /proc/$$/cmdline)

Cependant, cela pourrait ne pas être mieux que simplement:

echo $0

À propos de l'exécution d'un shell réellement différent de celui indiqué par le nom, une idée est de demander la version du shell en utilisant le nom que vous avez obtenu précédemment:

<some_shell> --version

sh semble échouer avec le code de sortie 2 tandis que d'autres donnent quelque chose d'utile (mais je ne suis pas en mesure de tout vérifier car je ne les ai pas):

$ sh --version
sh: 0: Illegal option --
echo $?
2

1

Ce n'est pas une solution très propre, mais elle fait ce que vous voulez.

# 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

Vous pouvez maintenant utiliser $(getshell) --version .

Cela ne fonctionne cependant que sur les shells de type KornShell (ksh).


1
"Cela ne fonctionne, cependant, que sur des shells de type ksh." Voulez-vous dire que je dois vérifier le shell avant d' exécuter cela? Hmm ...
jpaugh

@jpaugh, les listes doivent être pris en charge par le shell de sourcing ce code, ce qui est le cas dash, yashetc. En règle générale, si vous utilisez bash, zsh, ksh, quel que soit - vous ne devriez pas se soucier de ces choses.
theoden8

Donc, vous comptez bash comme "ksh-like"? Je l'ai. Cela a plus de sens
jpaugh

@jpaugh, eh bien, c'est ce que je voulais dire parce que kshl'ensemble de fonctionnalités est principalement un sous-ensemble de bashfonctionnalités (mais je n'ai pas vérifié cela soigneusement cependant).
theoden8

1

Procédez comme suit pour savoir si votre shell utilise Dash / Bash.

ls –la /bin/sh:

  • si le résultat est /bin/sh -> /bin/bash==> alors votre shell utilise Bash.

  • si le résultat est /bin/sh ->/bin/dash==> alors votre shell utilise Dash.

Si vous souhaitez passer de Bash à Dash ou vice-versa, utilisez le code ci-dessous:

ln -s /bin/bash /bin/sh (changer le shell en Bash)

Remarque : Si la commande ci-dessus entraîne une erreur indiquant que / bin / sh existe déjà, supprimez / bin / sh et réessayez.


0

Et j'ai trouvé ça

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

Cela ne fonctionnera que sous Linux ! Pas MacOS, ni BSD !!
F.Hauri

-1

Veuillez utiliser la commande ci-dessous:

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

Le premier est un non-sens, echo $SHELLfait ce que vous essayez de faire et le fait bien. Le second n'est pas bon non plus, car $SHELLla variable d'environnement contient un shell par défaut pour un utilisateur actuel, pas un shell en cours d'exécution. Si j'ai par exemple bashdéfini comme shell par défaut, exécutez zshet echo $SHELL, il s'imprimera bash.
David Ferenczy Rogožan

Vous avez raison Dawid Ferenczy, nous pouvons utiliser la commande ps pour déterminer le shell actuel. [# ps -p $$ | queue -1 | awk '{print $ 4}'].
Ranjithkumar T

echo $SHELLpeut être des ordures: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
SaltwaterC

-2

Celui-ci fonctionne bien sur Red Hat Linux (RHEL), macOS, BSD et certains AIX :

ps -T $$ | awk 'NR==2{print $NF}' 

alternativement, le suivant devrait également fonctionner si pstree est disponible,

pstree | egrep $$ | awk 'NR==2{print $NF}'
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.