¿Cómo puedo determinar el shell actual en el que estoy trabajando?
¿ ps
Sería suficiente la salida del comando?
¿Cómo se puede hacer esto en diferentes sabores de Unix?
¿Cómo puedo determinar el shell actual en el que estoy trabajando?
¿ ps
Sería suficiente la salida del comando?
¿Cómo se puede hacer esto en diferentes sabores de Unix?
Respuestas:
Hay tres enfoques para encontrar el nombre del ejecutable del shell actual:
Tenga en cuenta que los tres enfoques pueden ser engañados si el ejecutable del shell es /bin/sh
, pero en realidad es un cambio de nombre bash
, por ejemplo (lo que sucede con frecuencia).
Por lo tanto, su segunda pregunta sobre si la ps
salida funcionará se responde con " no siempre ".
echo $0
- imprimirá el nombre del programa ... que en el caso del shell es el shell real.
ps -ef | grep $$ | grep -v grep
- esto buscará el ID del proceso actual en la lista de procesos en ejecución. Dado que el proceso actual es el shell, se incluirá.
Esto no es 100% confiable, ya que es posible que tenga otros procesos cuya ps
lista incluya el mismo número que el ID de proceso del shell, especialmente si ese ID es un número pequeño (por ejemplo, si el PID del shell es "5", puede encontrar procesos llamados "java5" o "perl5" en la misma grep
salida!). Este es el segundo problema con el enfoque "ps", además de no poder confiar en el nombre del shell.
echo $SHELL
- La ruta al shell actual se almacena como la SHELL
variable para cualquier shell. La advertencia para esto es que si inicia un shell explícitamente como un subproceso (por ejemplo, no es su shell de inicio de sesión), obtendrá el valor de su shell de inicio de sesión. Si es una posibilidad, use el enfoque ps
o $0
.
Sin embargo, si el ejecutable no coincide con su shell real (por ejemplo, en /bin/sh
realidad es bash o ksh), necesita heurística. Aquí hay algunas variables ambientales específicas para varios shells:
$version
está configurado en tcsh
$BASH
se establece en bash
$shell
(minúscula) se establece en el nombre real del shell en csh o tcsh
$ZSH_NAME
está configurado en zsh
ksh tiene $PS3
y $PS4
establece, mientras que el shell Bourne normal ( sh
) solo tiene $PS1
y $PS2
establece. Esto parece generalmente como el más difícil de distinguir - la única diferencia en todo el conjunto de variables de entorno entre sh
y ksh
hemos instalado en Solaris es Boxen $ERRNO
, $FCEDIT
, $LINENO
, $PPID
, $PS3
, $PS4
, $RANDOM
, $SECONDS
, y $TMOUT
.
echo ${.sh.version}
devuelve "Mala sustitución". Vea mi solución arriba
ps -ef | grep …
... Esto no es 100% fiable como ...” Usando una expresión regular simple a través de egrep
o grep -e
fácilmente puede traer la fiabilidad hasta para-todo-intenciones-y-fines 100%: ps -ef | egrep "^\s*\d+\s+$$\s+"
. Se ^
asegura de que estamos comenzando desde el principio de la línea, se \d+
come el UID, $$
coincide con el PID y el \s*
y \s+
representa y garantiza el espacio en blanco entre las otras partes.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
debería funcionar en cualquier lugar donde las soluciones involucren ps -ef
y lo grep
hagan (en cualquier variante de Unix que admita las opciones POSIXps
) y no sufrirá los falsos positivos introducidos por grepping para una secuencia de dígitos que puede aparecer en otros lugares.
ps
cual puede que no se entienda, -p
por lo que es posible que deba usarla /bin/ps -p $$
.
$$
excepto los fish
que tendrías que usar ps -p %self
.
/bin/ps
. ps
podría instalarse fácilmente (en realidad es bastante normal hoy en día) /usr/bin
. $(which ps) -p $$
Es una mejor manera. Por supuesto, esto no funcionará en peces, y posiblemente en otras conchas. Creo que está (which ps) -p %self
en el pescado.
readlink /proc/$$/exe
sh
es emulado por bash
, ps -p te da /usr/bin/bash
incluso lo ejecutas comosh
Tratar
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 siguiente línea en mi script tiene el equivalente para csh.)
-q
lugar de -p
:SHELL=$(ps -ocomm= -q $$)
Si solo quiere asegurarse de que el usuario invoque un 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 esta línea, el script se ejecuta usando bash incluso si se comenzó a usar ksh o sh. Mi caso de uso no necesita argumentos de línea de comando, pero podrían agregarse después $0
si es necesario.
Puedes probar:
ps | grep `echo $$` | awk '{ print $4 }'
O:
echo $SHELL
/pattern/ { action }
lo hará?
$SHELL
La variable de entorno contiene un shell, que está configurado como predeterminado para un usuario actual. No refleja un shell, que se está ejecutando actualmente. También es mejor usar ps -p $$
que grepping $$ debido a falsos positivos.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
No siempre es necesario mostrar el shell actual. Solo refleja el shell predeterminado que se invocará.
Para probar lo anterior, digamos que bash
es el shell predeterminado, intente echo $SHELL
, y luego en el mismo terminal, ingrese a otro shell ( KornShell (ksh) por ejemplo) e intente $SHELL
. Verá el resultado como bash en ambos casos.
Para obtener el nombre del shell actual, use cat /proc/$$/cmdline
. Y la ruta al shell ejecutable por readlink /proc/$$/exe
.
/proc
.
ps es el método más confiable. No se garantiza que se establezca la variable de entorno SHELL e incluso si lo es, se puede suplantar fácilmente.
Tengo un simple truco para encontrar el shell actual. Simplemente escriba una cadena aleatoria (que no es un comando). Fallará y devolverá un error "no encontrado", pero al comienzo de la línea dirá de qué shell se trata:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
echo 'aaaa' > script; chmod +x script; ./script
da./script.sh: 1: aaaa: not found
Lo siguiente siempre dará el shell real utilizado: obtiene el nombre del ejecutable real y no el nombre del shell (es decir, en ksh93
lugar de ksh
, etc.). Para /bin/sh
, mostrará el shell real utilizado, es decir dash
.
ls -l /proc/$$/exe | sed 's%.*/%%'
Sé que hay muchos que dicen que la ls
salida nunca debe procesarse, pero ¿cuál es la probabilidad de que tenga un shell que está usando que se nombra con caracteres especiales o se coloca en un directorio con caracteres especiales? Si este sigue siendo el caso, hay muchos otros ejemplos de hacerlo de manera diferente.
Como señaló Toby Speight , esta sería una forma más adecuada y limpia de lograr lo mismo:
basename $(readlink /proc/$$/exe)
/proc
. No todo el mundo es una caja de Linux.
basename $(readlink /proc/$$/exe)
a ls
+ sed
+ echo
.
ash -> /bin/busybox
, esto dará / bin / busybox.
He intentado muchos enfoques diferentes y el mejor para mí es:
ps -p $$
También funciona bajo Cygwin y no puede producir falsos positivos como grepping PID. Con un poco de limpieza, genera solo un nombre ejecutable (en Cygwin con ruta):
ps -p $$ | tail -1 | awk '{print $NF}'
Puede crear una función para no tener que memorizarla:
# Print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
... y luego simplemente ejecuta shell
.
Fue probado bajo Debian y Cygwin.
ps
, tail
y gawk
, cmd no se define $$
como su PID, por lo que definitivamente no puede funcionar bajo el cmd simple.
ps -p$$ -o comm=
? POSIX dice que especificar todos los encabezados vacíos suprime el encabezado por completo. Todavía estamos fallando (como todas las ps
respuestas) cuando nos obtiene un script ejecutado directamente (por ejemplo #!/bin/sh
).
Mi variante para imprimir el proceso padre:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
No ejecute aplicaciones innecesarias cuando AWK pueda hacerlo por usted.
awk
, cuando ps -p "$$" -o 'comm='
puede hacerlo por usted?
Hay muchas formas de averiguar el shell y su versión correspondiente. Aquí hay algunos que me funcionaron.
Sencillo
Enfoque hackish
$> ******* (Escriba un conjunto de caracteres aleatorios y en la salida obtendrá el nombre del shell. En mi caso -bash: chapter2-a-sample-isomorphic-app: comando no encontrado )
Siempre que /bin/sh
admita el estándar POSIX y su sistema tenga lsof
instalado el comando, una posible alternativa a lo que lsof
podría ser en este caso pid2path
, también puede usar (o adaptar) el siguiente script que imprime las rutas completas:
#!/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
opción (-> ignorar entorno) env
en la línea donde verifica si lsof
está disponible. falla con: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
Si solo desea verificar que está ejecutando (una versión particular de) Bash, la mejor manera de hacerlo es usar la $BASH_VERSINFO
variable de matriz. Como una variable de matriz (solo lectura) no se puede establecer en el entorno, por lo que puede estar seguro de que proviene (si es que lo hace) del shell actual.
Sin embargo, dado que Bash tiene un comportamiento diferente cuando se invoca como sh
, también es necesario verificar que la $BASH
variable de entorno termina con /bash
.
En un script que escribí que usa nombres de funciones con -
(no subrayado), y depende de matrices asociativas (agregadas en Bash 4), tengo la siguiente comprobación de cordura (con un útil mensaje de error del usuario):
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
Podría omitir la comprobación funcional paranoica de las funciones en el primer caso, y simplemente asumir que las futuras versiones de Bash serían compatibles.
Ninguna de las respuestas funcionó con fish
shell (no tiene las variables $$
o $0
).
Esto funciona para mí (probado en sh
, bash
, fish
, ksh
, csh
, true
, tcsh
, y zsh
; openSUSE 13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
Este comando genera una cadena como bash
. Aquí solo estoy usando ps
, tail
y sed
(sin extensiones GNU; intente agregar --posix
para verificarlo). Todos son comandos POSIX estándar. Estoy seguro de que tail
se puede eliminar, pero mi sed
fu no es lo suficientemente fuerte como para hacer esto.
Me parece que esta solución no es muy portátil ya que no funciona en OS X. :(
sed: invalid option -- 'E'
en bash 3.2.51 y tcsh 6.15.00
Mi solución:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
Esto debería ser portátil en diferentes plataformas y shells. Se utiliza ps
como otras soluciones, pero no se basa en sed
o awk
filtra la basura de las tuberías y de ps
sí misma para que el shell siempre sea la última entrada. De esta manera, no necesitamos confiar en variables PID no portátiles o elegir las líneas y columnas correctas.
He probado en Debian y macOS con Bash, Z shell ( zsh
) y fish (que no funciona con la mayoría de estas soluciones sin cambiar la expresión específicamente para fish, porque usa una variable PID diferente).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
no es confiable. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
es mejor, pero ¿por qué no solo usar ps -p $$
?
En Mac OS X (y FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
, y me dio -bash
.
mutt
...: -b
No es necesario agrupar el PID de la salida de "ps", ya que puede leer la línea de comando correspondiente para cualquier PID desde la estructura del directorio / proc
echo $(cat /proc/$$/cmdline)
Sin embargo, eso podría no ser mejor que simplemente:
echo $0
Acerca de ejecutar un shell realmente diferente al que indica el nombre, una idea es solicitar la versión del shell utilizando el nombre que obtuvo anteriormente:
<some_shell> --version
sh
parece fallar con el código de salida 2 mientras que otros dan algo útil (pero no puedo verificarlo todo porque no los tengo):
$ sh --version
sh: 0: Illegal option --
echo $?
2
Esta no es una solución muy limpia, pero hace lo que quieres.
# 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
Ahora puedes usar $(getshell) --version
.
Sin embargo, esto funciona solo en shells similares a KornShell (ksh).
dash
, yash
etc. Normalmente, si está utilizando bash
, zsh
, ksh
, lo que sea - no debe preocuparse por esas cosas.
ksh
el conjunto de características es principalmente un subconjunto de bash
características (aunque no lo he comprobado a fondo).
Haga lo siguiente para saber si su shell está usando Dash / Bash.
ls –la /bin/sh
:
si el resultado es /bin/sh -> /bin/bash
==> Entonces su shell está usando Bash.
si el resultado es /bin/sh ->/bin/dash
==> Entonces su shell está usando Dash.
Si desea cambiar de Bash a Dash o viceversa, use el siguiente código:
ln -s /bin/bash /bin/sh
(cambiar shell a Bash)
Nota : Si el comando anterior da como resultado un error que dice, / bin / sh ya existe, elimine / bin / sh e intente nuevamente.
Utilice amablemente el siguiente comando:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
hace lo que estás tratando de hacer y hazlo bien. El segundo tampoco es bueno, porque $SHELL
la variable de entorno contiene un shell predeterminado para un usuario actual, no un shell actualmente en ejecución. Si, por ejemplo, lo bash
configuro como shell predeterminado, ejecutar zsh
y echo $SHELL
, se imprimirá bash
.
echo $SHELL
puede ser basura: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Este funciona bien en Red Hat Linux (RHEL), macOS, BSD y algunos AIX :
ps -T $$ | awk 'NR==2{print $NF}'
alternativamente, el siguiente también debería funcionar si pstree está disponible,
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
Probablemente, probar las capacidades particulares (por ejemplo, ¿hace sustitución?) Es más portátil que encontrar el nombre del shell. La costumbre local puede hacer que ejecute algo llamado/bin/sh
que podría ser ash, dash, bash, etc.