Comment puis-je déterminer le shell actuel sur lequel je travaille?
La sortie de la ps
commande suffirait-elle à elle seule?
Comment cela peut-il être fait dans différentes versions d'Unix?
Comment puis-je déterminer le shell actuel sur lequel je travaille?
La sortie de la ps
commande suffirait-elle à elle seule?
Comment cela peut-il être fait dans différentes versions d'Unix?
Réponses:
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 ps
sortie fera l'affaire est répondue par " pas toujours ".
echo $0
- affichera le nom du programme ... qui dans le cas du shell est le shell réel.
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 ps
liste 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 grep
sortie!). C'est le deuxième problème avec l'approche "ps", en plus de ne pas pouvoir s'appuyer sur le nom du shell.
echo $SHELL
- Le chemin vers le shell courant est stocké comme SHELL
variable 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 ps
ou $0
.
Si, cependant, l'exécutable ne correspond pas à votre shell réel (par exemple, /bin/sh
est 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 $PS3
et $PS4
mis, tandis que le shell Bourne normal ( sh
) n'a que $PS1
et $PS2
mis. Cela semble généralement comme les plus difficiles à distinguer - la seule différence dans l'ensemble des variables d'environnement entre sh
et ksh
nous avons installé sur Solaris Boxen est $ERRNO
, $FCEDIT
, $LINENO
, $PPID
, $PS3
, $PS4
, $RANDOM
, $SECONDS
et $TMOUT
.
echo ${.sh.version}
renvoie "Bad Substitution". Voir ma solution ci
ps -ef | grep …
... Ce n'est pas fiable à 100% que ... » En utilisant une expression régulière simple via egrep
ou grep -e
peut 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.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
devrait fonctionner partout où les solutions impliquent ps -ef
et grep
font (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.
ps
qui peut ne pas comprendre -p
, vous devrez donc peut-être l'utiliser /bin/ps -p $$
.
$$
sauf ceux fish
avec lesquels vous auriez à utiliser ps -p %self
.
/bin/ps
. ps
pourrait 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 %self
dans le poisson.
readlink /proc/$$/exe
sh
est émulé par bash
, ps -p vous donne /usr/bin/bash
même si vous l'exécutez commesh
Essayer
ps -p $$ -oargs=
ou
ps -p $$ -ocomm=
ps -o fname --no-headers $$
.
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
. (La ligne suivante de mon script a l'équivalent de csh.)
-q
place de -p
:SHELL=$(ps -ocomm= -q $$)
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
#!/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 $0
si nécessaire.
Tu peux essayer:
ps | grep `echo $$` | awk '{ print $4 }'
Ou:
echo $SHELL
/pattern/ { action }
va faire?
$SHELL
La 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.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
n'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 bash
est 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
.
/proc
.
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.
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
echo 'aaaa' > script; chmod +x script; ./script
donne./script.sh: 1: aaaa: not found
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 ls
sortie 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)
/proc
. Tout le monde n'est pas une boîte Linux.
basename $(readlink /proc/$$/exe)
à ls
+ sed
+ echo
.
ash -> /bin/busybox
, cela donnera / bin / busybox.
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.
ps
, tail
et gawk
, cmd ne se définit pas $$
comme étant un PID, il ne peut donc certainement pas fonctionner sous la cmd standard.
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 ps
réponses) lorsque nous sommes sourcés par un script exécuté directement (par exemple #!/bin/sh
).
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.
awk
, quand ps -p "$$" -o 'comm='
peut-il le faire pour vous?
Il existe de nombreuses façons de découvrir le shell et sa version correspondante. Voici quelques-uns qui ont fonctionné pour moi.
Simple
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 )
À condition que votre /bin/sh
supporte la norme POSIX et que votre système ait la lsof
commande installée - une alternative possible à lsof
pourrait 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}'
-i
option (-> ignorer l'environnement) env
dans la ligne où vous vérifiez lsof
sa disponibilité. il échoue avec: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
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_VERSINFO
variable 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 $BASH
fin 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.
Aucune des réponses n'a fonctionné avec le fish
shell (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
, tail
et sed
(sans extesions GNU, essayez d'ajouter --posix
à vérifier). Ce sont toutes des commandes POSIX standard. Je suis sûr qu'il tail
peut être retiré, mais mon sed
fu 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. :(
sed: invalid option -- 'E'
sur bash 3.2.51 et tcsh 6.15.00
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 ps
comme les autres solutions, mais il ne s'appuie pas sur sed
ou awk
et filtre les fichiers indésirables de la tuyauterie et de ps
lui - 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).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
n'est pas fiable. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
c'est mieux, mais pourquoi ne pas simplement l'utiliser ps -p $$
?
Sous Mac OS X (et FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
, et ça m'a donné -bash
.
mutt
...: -b
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
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).
dash
, yash
etc. En règle générale, si vous utilisez bash
, zsh
, ksh
, quel que soit - vous ne devriez pas se soucier de ces choses.
ksh
l'ensemble de fonctionnalités est principalement un sous-ensemble de bash
fonctionnalités (mais je n'ai pas vérifié cela soigneusement cependant).
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.
Veuillez utiliser la commande ci-dessous:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
fait ce que vous essayez de faire et le fait bien. Le second n'est pas bon non plus, car $SHELL
la 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 bash
défini comme shell par défaut, exécutez zsh
et echo $SHELL
, il s'imprimera bash
.
echo $SHELL
peut être des ordures: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
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}'
!
substitution?) Est probablement plus portable que de trouver le nom du shell. La coutume locale pourrait vous faire exécuter quelque chose nommé/bin/sh
qui pourrait en fait être ash, dash, bash, etc.