Como posso determinar o shell atual em que estou trabalhando?
ps
Somente a saída do comando seria suficiente?
Como isso pode ser feito em diferentes sabores do Unix?
Como posso determinar o shell atual em que estou trabalhando?
ps
Somente a saída do comando seria suficiente?
Como isso pode ser feito em diferentes sabores do Unix?
Respostas:
Existem três abordagens para encontrar o nome do executável do shell atual:
Observe que todas as três abordagens podem ser enganadas se o executável do shell for /bin/sh
, mas é realmente renomeado bash
, por exemplo (o que geralmente acontece).
Assim, sua segunda pergunta sobre se a ps
saída servirá é respondida com " nem sempre ".
echo $0
- imprimirá o nome do programa ... que, no caso do shell, é o shell real.
ps -ef | grep $$ | grep -v grep
- isso procurará o ID do processo atual na lista de processos em execução. Como o processo atual é o shell, ele será incluído.
Isso não é 100% confiável, pois você pode ter outros processos cuja ps
lista inclui o mesmo número que o ID do processo do shell, especialmente se esse ID for um número pequeno (por exemplo, se o PID do shell for "5", você poderá encontrar processos chamados "java5" ou "perl5" na mesma grep
saída!). Este é o segundo problema com a abordagem "ps", além de não poder confiar no nome do shell.
echo $SHELL
- O caminho para o shell atual é armazenado como a SHELL
variável para qualquer shell. A ressalva é que, se você iniciar um shell explicitamente como um subprocesso (por exemplo, não é o seu shell de login), obterá o valor do shell de login. Se for possível, use a abordagem ps
ou $0
.
Se, no entanto, o executável não corresponder ao seu shell real (por exemplo, /bin/sh
é realmente bash ou ksh), você precisará de heurísticas. Aqui estão algumas variáveis ambientais específicas para várias conchas:
$version
está definido no tcsh
$BASH
é definido no bash
$shell
(minúsculo) é definido como o nome real do shell em csh ou tcsh
$ZSH_NAME
está definido no zsh
O ksh possui $PS3
e $PS4
define, enquanto o shell Bourne normal ( sh
) apenas possui $PS1
e $PS2
define. Isso geralmente parece ser o mais difícil de distinguir - a única diferença em todo o conjunto de variáveis de ambiente entre sh
e ksh
temos instalado no Solaris boxen é $ERRNO
, $FCEDIT
, $LINENO
, $PPID
, $PS3
, $PS4
, $RANDOM
, $SECONDS
, e $TMOUT
.
echo ${.sh.version}
retorna "Substituição incorreta". Veja minha solução acima
ps -ef | grep …
… Isso não é 100% confiável como…” O uso de uma expressão regular simples via egrep
ou grep -e
pode facilmente trazer a confiabilidade até 100% para todos os efeitos - ps -ef | egrep "^\s*\d+\s+$$\s+"
. As ^
marcas certeza de que estamos a partir do início da linha, os \d+
come-se o UID, o $$
corresponde ao PID, eo \s*
e \s+
conta para e garantir espaço em branco entre as outras partes.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
deve funcionar em qualquer lugar que as soluções que envolvem ps -ef
e funcionem grep
(em qualquer variante Unix que suporte opções POSIXps
) e não sofrerá com os falsos positivos introduzidos pelo grepping para uma sequência de dígitos que pode aparecer em outro lugar.
ps
que pode não ser entendida, -p
portanto, você pode precisar usá-lo /bin/ps -p $$
.
$$
exceto as fish
que você precisaria usar ps -p %self
.
/bin/ps
. ps
poderia facilmente (atualmente é bastante normal hoje em dia) ser instalado no /usr/bin
. $(which ps) -p $$
é uma maneira melhor. Obviamente, isso não funcionará em peixes e, possivelmente, em outras conchas. Eu acho que é (which ps) -p %self
em peixe.
readlink /proc/$$/exe
sh
for emulado por bash
, ps -p lhe dará /usr/bin/bash
até você executá-lo como #sh
Tentar
ps -p $$ -oargs=
ou
ps -p $$ -ocomm=
ps -o fname --no-headers $$
.
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
. (A próxima linha no meu script tem o equivalente para csh.)
-q
vez de -p
:SHELL=$(ps -ocomm= -q $$)
Se você apenas deseja garantir que o usuário esteja chamando um script com o 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
. Com esta linha, o script é executado usando o bash, mesmo que você comece a usar o ksh ou sh. Meu caso de uso não precisa de argumentos de linha de comando, mas eles podem ser adicionados depois, $0
se necessário.
Podes tentar:
ps | grep `echo $$` | awk '{ print $4 }'
Ou:
echo $SHELL
/pattern/ { action }
fará?
$SHELL
A variável de ambiente contém um shell, que é configurado como padrão para um usuário atual. Ele não reflete um shell, que está sendo executado no momento. Também é melhor usar do ps -p $$
que grepping $$ por causa de falsos positivos.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
nem sempre é necessário mostrar o shell atual. Ele reflete apenas o shell padrão a ser chamado.
Para testar o que bash
foi dito acima, digamos que é o shell padrão, tente echo $SHELL
e, no mesmo terminal, entre em outro shell ( KornShell (ksh), por exemplo) e tente $SHELL
. Você verá o resultado como bash nos dois casos.
Para obter o nome do shell atual, use cat /proc/$$/cmdline
. E o caminho para o shell executável por readlink /proc/$$/exe
.
/proc
.
ps é o método mais confiável. Não é garantido que a variável de ambiente SHELL seja configurada e, mesmo que seja, ela pode ser facilmente falsificada.
Eu tenho um truque simples para encontrar o shell atual. Basta digitar uma sequência aleatória (que não é um comando). Ele falhará e retornará um erro "não encontrado", mas no início da linha, ele dirá qual 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
O seguinte sempre fornecerá o shell real usado - ele obtém o nome do executável real e não o nome do shell (por exemplo, em ksh93
vez de ksh
etc.). Pois /bin/sh
, mostrará o shell atual usado, ie dash
.
ls -l /proc/$$/exe | sed 's%.*/%%'
Sei que muitos dizem que a ls
saída nunca deve ser processada, mas qual é a probabilidade de você usar um shell nomeado com caracteres especiais ou colocado em um diretório nomeado com caracteres especiais? Se esse ainda for o caso, há muitos outros exemplos de como fazê-lo de maneira diferente.
Conforme apontado por Toby Speight , essa seria uma maneira mais adequada e mais limpa de conseguir o mesmo:
basename $(readlink /proc/$$/exe)
/proc
. Nem todo mundo é um Linux.
basename $(readlink /proc/$$/exe)
a ls
+ sed
+ echo
.
ash -> /bin/busybox
, isso fornecerá / bin / busybox.
Eu tentei muitas abordagens diferentes e a melhor para mim é:
ps -p $$
Também funciona com Cygwin e não pode produzir falsos positivos como grepping de PID. Com alguma limpeza, ele gera apenas um nome executável (em Cygwin com path):
ps -p $$ | tail -1 | awk '{print $NF}'
Você pode criar uma função para não precisar memorizá-la:
# Print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
... e então apenas execute shell
.
Foi testado no Debian e Cygwin.
ps
, tail
e gawk
, cmd não define $$
como PID, então definitivamente não pode funcionar sob o cmd comum.
ps -p$$ -o comm=
? O POSIX diz que especificar todos os cabeçalhos vazios suprime completamente o cabeçalho. Ainda estamos falhando (como todas as ps
respostas) quando somos originados por um script executado diretamente (por exemplo #!/bin/sh
).
Minha variante na impressão do processo pai:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
Não execute aplicativos desnecessários quando o AWK puder fazer isso por você.
awk
, quando ps -p "$$" -o 'comm='
pode fazer isso por você?
Existem muitas maneiras de descobrir o shell e sua versão correspondente. Aqui estão alguns que funcionaram para mim.
Direto
Abordagem Hackish
$> ******* (Digite um conjunto de caracteres aleatórios e, na saída, você obterá o nome do shell. No meu caso -bash: chapter2-a-sample-isomorphic-app: comando não encontrado )
Desde que você /bin/sh
suporte o padrão POSIX e seu sistema possua o lsof
comando instalado - uma possível alternativa possível lsof
neste caso pid2path
- você também pode usar (ou adaptar) o seguinte script que imprime caminhos completos:
#!/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
(-> ignore environment) env
na linha em que você verifica a lsof
disponibilidade. ele falha com: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
Se você quiser apenas verificar se está executando (uma versão específica do) Bash, a melhor maneira de fazer isso é usar a $BASH_VERSINFO
variável de matriz. Como uma variável de matriz (somente leitura), ela não pode ser definida no ambiente, portanto, você pode ter certeza de que está vindo (se houver) do shell atual.
No entanto, como o Bash tem um comportamento diferente quando chamado como sh
, você também precisa verificar a $BASH
variável de ambiente que termina com /bash
.
Em um script que escrevi que usa nomes de funções com -
(sem sublinhado) e depende de matrizes associativas (adicionadas no Bash 4), tenho a seguinte verificação de integridade (com mensagem de erro útil do usuário):
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
Você pode omitir a verificação funcional um tanto paranóica de recursos no primeiro caso e apenas assumir que versões futuras do Bash seriam compatíveis.
Nenhuma das respostas funcionou com o fish
shell (ele não possui as variáveis $$
ou $0
).
Isso funciona para mim (testadas sh
, bash
, fish
, ksh
, csh
, true
, tcsh
, e zsh
; openSUSE 13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
Este comando gera uma string como bash
. Aqui eu só estou usando ps
, tail
e sed
(sem extesions GNU; tentar adicionar --posix
para verificá-lo). Todos eles são comandos POSIX padrão. Tenho certeza que tail
pode ser removido, mas meu sed
fu não é forte o suficiente para fazer isso.
Parece-me que esta solução não é muito portátil, pois não funciona no OS X. :(
sed: invalid option -- 'E'
no bash 3.2.51 e tcsh 6.15.00
Minha solução:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
Isso deve ser portátil em diferentes plataformas e shells. Ele usa ps
como outras soluções, mas não depende sed
ou awk
filtra o lixo da tubulação e ps
ele próprio, para que o shell sempre seja a última entrada. Dessa forma, não precisamos confiar em variáveis PID não portáveis ou escolher as linhas e colunas certas.
Eu testei no Debian e no macOS com Bash, Z shell ( zsh
) e fish (que não funciona com a maioria dessas soluções sem alterar a expressão especificamente para fish, porque ele usa uma variável PID diferente).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
não é confiável. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
é melhor, mas por que não usar ps -p $$
?
No Mac OS X (e FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
, e isso me deu -bash
.
mutt
...: -b
Não é necessário receber o PID da saída de "ps", porque você pode ler a respectiva linha de comando para qualquer PID da estrutura de diretório / proc:
echo $(cat /proc/$$/cmdline)
No entanto, isso pode não ser melhor do que simplesmente:
echo $0
Sobre a execução de um shell realmente diferente do que o nome indica, uma idéia é solicitar a versão do shell usando o nome que você obteve anteriormente:
<some_shell> --version
sh
parece falhar com o código de saída 2, enquanto outros fornecem algo útil (mas não consigo verificar tudo porque não os tenho):
$ sh --version
sh: 0: Illegal option --
echo $?
2
Esta não é uma solução muito limpa, mas faz o que você deseja.
# 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
Agora você pode usar $(getshell) --version
.
Isso funciona, porém, apenas em conchas do tipo KornShell (ksh).
dash
, yash
etc. Normalmente, se você estiver usando bash
, zsh
, ksh
, seja qual for - você deve se preocupam com essas coisas.
ksh
o conjunto de recursos é principalmente um subconjunto de bash
recursos (ainda não o verifiquei completamente).
Faça o seguinte para saber se o seu shell está usando o Dash / Bash.
ls –la /bin/sh
:
se o resultado for /bin/sh -> /bin/bash
==> Então seu shell está usando o Bash.
se o resultado for /bin/sh ->/bin/dash
==> Então seu shell está usando o Dash.
Se você deseja mudar de Bash para Dash ou vice-versa, use o código abaixo:
ln -s /bin/bash /bin/sh
(mude o shell para Bash)
Nota : Se o comando acima resultar em um erro dizendo: / bin / sh já existe, remova o / bin / sh e tente novamente.
Por favor, use o comando abaixo:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
faz o que você está tentando fazer e faz bem. O segundo também não é bom, porque $SHELL
a variável de ambiente contém shell padrão para um usuário atual, não um shell em execução no momento. Se eu tiver bash
definido, por exemplo , como shell padrão, execute zsh
e echo $SHELL
, ele será impresso bash
.
echo $SHELL
pode ser lixo: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Este funciona bem no Red Hat Linux (RHEL), macOS, BSD e alguns AIXes :
ps -T $$ | awk 'NR==2{print $NF}'
alternativamente, o seguinte também deve funcionar se pstree estiver disponível,
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
substitui?) É provavelmente mais portátil do que encontrar o nome do shell. Costume local pode ter você correndo algo chamado/bin/sh
que poderia realmente ser cinzas, traço, festança, etc.