Como determinar o shell atual em que estou trabalhando


634

Como posso determinar o shell atual em que estou trabalhando?

psSomente a saída do comando seria suficiente?

Como isso pode ser feito em diferentes sabores do Unix?


9
Testar recursos específicos (por exemplo, !substitui?) É provavelmente mais portátil do que encontrar o nome do shell. Costume local pode ter você correndo algo chamado /bin/shque poderia realmente ser cinzas, traço, festança, etc.
msw

1
@ msw: Parece um bom comentário, exceto que me deixa pensando "como?".
#

Parece que não há uma resposta simples para esta pergunta. Se não pudermos consultar o shell, talvez a melhor abordagem seja sempre especificar o shell. Não tenho certeza de que isso sempre seja possível, mas talvez seja mais fácil do que as pessoas geralmente supõem.
#


@ Aniket, não ajuda tanto quanto você pensa - está interessado apenas em processos de shell interativos .
Toby Speight

Respostas:


794
  • 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 pssaída servirá é respondida com " nem sempre ".

    1. echo $0 - imprimirá o nome do programa ... que, no caso do shell, é o shell real.

    2. 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 pslista 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 grepsaída!). Este é o segundo problema com a abordagem "ps", além de não poder confiar no nome do shell.

    3. echo $SHELL- O caminho para o shell atual é armazenado como a SHELLvariá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 psou $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 $PS3e $PS4define, enquanto o shell Bourne normal ( sh) apenas possui $PS1e $PS2define. Isso geralmente parece ser o mais difícil de distinguir - a única diferença em todo o conjunto de variáveis de ambiente entre she kshtemos instalado no Solaris boxen é $ERRNO, $FCEDIT, $LINENO, $PPID, $PS3, $PS4, $RANDOM, $SECONDS, e $TMOUT.


$ {. sh.version} está definido em ksh93
fpmurphy

14
ps -p $$como Matthew Slattery aponta. Para ksh: echo $KSH_VERSIONou echo ${.sh.version}.
Pausado até novo aviso.

@ Dennish - meu ksh agora não tem KSH_VERSION definido. e echo ${.sh.version}retorna "Substituição incorreta". Veja minha solução acima
DVK

3
ps -ef | grep …… Isso não é 100% confiável como…” O uso de uma expressão regular simples via egrepou grep -epode 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.
Slipp D. Thompson

2
@ SlippD.Thompson não funcionou no GNU / Linux. Mas isso parece funcionar:ps -ef | awk '$2==pid' pid=$$
jarno

98

ps -p $$

deve funcionar em qualquer lugar que as soluções que envolvem ps -efe 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.


12
Alguns shells possuem sua própria versão interna, psque pode não ser entendida, -pportanto, você pode precisar usá-lo /bin/ps -p $$.
Pausado até novo aviso.

13
Todas as conchas que eu conheço entendem, $$exceto as fishque você precisaria usar ps -p %self.
Pausado até novo aviso.

3
Na verdade, você não deve confiar em caminhos difíceis como /bin/ps. pspoderia 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 %selfem peixe.
Aron Cederholm

1
Em alguns sistemas mínimos, como um container docker debian-slim, o ps pode não estar lá. Nesse caso, essa abordagem ainda funciona:readlink /proc/$$/exe
mxmlnkn 28/02/19

se o seu shfor emulado por bash, ps -p lhe dará /usr/bin/bashaté você executá-lo como #sh
Ding-Yi Chen

45

Tentar

ps -p $$ -oargs=

ou

ps -p $$ -ocomm=

4
Este é um bom curto. Eu estava me usando ps -o fname --no-headers $$.
Anne van Rossum

Obrigado. Achei essa a melhor opção para usar em um script para proteger comandos específicos do bash test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash. (A próxima linha no meu script tem o equivalente para csh.)
craq

1
Descobri que se você fizer isso de dentro de um subshell, isso pode levar a linhas extras falsas, combinando o PID do pai e o processo de shell real. Para isso, uso em -qvez de -p:SHELL=$(ps -ocomm= -q $$)
Steve

35

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

1
Este deve ser o mais próximo do topo. Muito obrigado.
Alex Skrypnyk

4
Não deve estar mais perto do topo, porque não responde à pergunta. Se a pergunta for "Como verificar se o script está sendo executado no bash", eu voto a favor.
David Ferenczy Rogožan 14/10

2
@DawidFerenczy - Essa pergunta é o resultado principal quando você procura essa frase. A longo prazo, acho muito mais importante que as respostas respondam ao que as pessoas estão procurando do que ao que se tratava a pergunta original.
ArtOfWarfare

3
Além disso, a variável $ BASH é definida em tcsh se o tcsh for chamado a partir do bash
18446744073709551615

Isso é muito útil, obrigado. Apenas adaptado um pouco, colocar isso como a segunda linha depois #!/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, $0se necessário.
Joanis

20

Podes tentar:

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

Ou:

echo $SHELL

6
Qual é o ponto de grep seguido por awk, quando o /pattern/ { action }fará?
Jens

# no zshell alias shell = 'echo $ {SHELL: t}'
SergioAraujo 13/01

4
$SHELLA 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.
David Ferenczy Rogožan 14/10

O env $ SHELL. A variável aponta para o shell 'pai', conforme especificado na especificação POSIX: SHELL Essa variável deve representar um nome de caminho do interpretador de linguagem de comando preferido do usuário. Portanto, o valor de $ SHELL pode não ser o shell atual.
Theo

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

16

$SHELLnem sempre é necessário mostrar o shell atual. Ele reflete apenas o shell padrão a ser chamado.

Para testar o que bashfoi dito acima, digamos que é o shell padrão, tente echo $SHELLe, 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.


8
... Desde que você tenha /proc.
Tripleee

10

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.


12
+1 $ SHELL é o shell padrão para programas que precisam gerar um. Não reflete necessariamente o shell que está sendo executado no momento.
Jim Lewis

8

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

3
Não é bom em um script. echo 'aaaa' > script; chmod +x script; ./script./script.sh: 1: aaaa: not found
slim

6

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 ksh93vez de kshetc.). Pois /bin/sh, mostrará o shell atual usado, ie dash.

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

Sei que muitos dizem que a lssaí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)

3
Isso apenas erros em todos os Unices que não fornecem /proc. Nem todo mundo é um Linux.
Jens

5
E se você está no Linux, nós preferimos basename $(readlink /proc/$$/exe)a ls+ sed+ echo.
precisa saber é o seguinte

1
Isso fornecerá o nome do executável real , não o shell real . Quando o shell real é vinculado como um applet do busybox, digamos ash -> /bin/busybox, isso fornecerá / bin / busybox.
stepse

6

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.


Na minha configuração (Cygwin | Windows 7), sua resposta é a melhor e ps -p $$ | cauda -1 | O gawk '{print $ NF}' funciona mesmo a partir do cmd sem o $ bash. Observe gawk em vez de awk, pois o awk funciona apenas no bash.
WebComer

@WebComer Desculpe, não sei ao certo o que você quer dizer com " funciona mesmo no cmd sem o $ bash ". Mesmo se você tiver portas Windows de ps, taile gawk, cmd não define $$como PID, então definitivamente não pode funcionar sob o cmd comum.
David Ferenczy Rogožan 11/03/19

Por que não simplesmente 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 psrespostas) quando somos originados por um script executado diretamente (por exemplo #!/bin/sh).
Toby Speight

5

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ê.


2
Por que executar um desnecessário awk, quando ps -p "$$" -o 'comm='pode fazer isso por você?
Toby Speight

5

Existem muitas maneiras de descobrir o shell e sua versão correspondente. Aqui estão alguns que funcionaram para mim.

Direto

  1. $> echo $ 0 (fornece o nome do programa. No meu caso, a saída era -bash .)
  2. $> $ SHELL (Isso leva você para o shell e, no prompt, você obtém o nome e a versão do shell. No meu caso, bash3.2 $ .)
  3. $> echo $ SHELL (Isso fornecerá um caminho executável. No meu caso / bin / bash .)
  4. $> $ SHELL --version (Isso fornecerá informações completas sobre o software shell com o tipo de licença)

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 )


3

Desde que você /bin/shsuporte o padrão POSIX e seu sistema possua o lsofcomando instalado - uma possível alternativa possível lsofneste 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}'

1
isso funciona em peixes! mas, para mim, falha no bash, devido à opção -i(-> ignore environment) envna linha em que você verifica a lsofdisponibilidade. ele falha com: env -i PATH="${PATH}" type lsof->env: ‘type’: No such file or directory
hoijui 14/03

2

Se você quiser apenas verificar se está executando (uma versão específica do) Bash, a melhor maneira de fazer isso é usar a $BASH_VERSINFOvariá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 $BASHvariá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.


2

Nenhuma das respostas funcionou com o fishshell (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, taile sed(sem extesions GNU; tentar adicionar --posixpara verificá-lo). Todos eles são comandos POSIX padrão. Tenho certeza que tailpode ser removido, mas meu sedfu 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. :(


Eu recebo sed: invalid option -- 'E'no bash 3.2.51 e tcsh 6.15.00
craq

2

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 pscomo outras soluções, mas não depende sedou awkfiltra o lixo da tubulação e psele 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).



1

No Mac OS X (e FreeBSD):

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

2
Tentei isso enquanto usava zsh, e isso me deu -bash.
User137369

No meu sistema (agora), testado com bash e dash, esse retorno mutt...: -b
F. Hauri 05/02

1

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

1

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).


1
"Isso funciona, porém, apenas em conchas do tipo ksh". Você está dizendo que eu tenho que verificar o shell antes de executar isso? Hmm ...
jpaugh 17/08/16

@jpaugh, as listas devem ser suportados pelo shell terceirização este código, o que não é o caso dash, yashetc. Normalmente, se você estiver usando bash, zsh, ksh, seja qual for - você deve se preocupam com essas coisas.
theoden8

Então você está contando o bash como "ksh-like"? Entendi. Isso faz mais sentido
jpaugh 18/08/16

@ jpaugh, bem, foi isso que eu quis dizer, porque ksho conjunto de recursos é principalmente um subconjunto de bashrecursos (ainda não o verifiquei completamente).
theoden8

1

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.


0

E eu vim com isso

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

Isso funcionará apenas no Linux ! Não é MacOS, nem BSD !!
F. Hauri

-1

Por favor, use o comando abaixo:

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

O primeiro é um absurdo, echo $SHELLfaz o que você está tentando fazer e faz bem. O segundo também não é bom, porque $SHELLa 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 bashdefinido, por exemplo , como shell padrão, execute zshe echo $SHELL, ele será impresso bash.
David Ferenczy Rogožan 14/10

Você está correto, Dawid Ferenczy. Podemos usar o comando ps para determinar o shell atual. [# ps -p $$ | cauda -1 | awk '{print $ 4}'].
Ranjithkumar T

echo $SHELLpode ser lixo: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
SaltwaterC /

-2

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}'
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.