maneira compatível com checkbashisms para determinar o shell atual

18

No meu caso .profile, uso o código a seguir para garantir que aliases e funções relacionados ao Bash sejam originados apenas se o shell de logon for realmente o Bash :

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

Atualmente, estou colocando meus arquivos de configuração de shell, scripts e funções sob controle de versão. Recentemente, também iniciei o processo de remoção de Bashisms casuais de scripts de shell que não se beneficiam de recursos específicos do Bash, por exemplo, substituindo function funcname()por funcname().

Para o meu repositório de arquivos shell, configurei um gancho de pré-confirmação que executa o checkbashismsutilitário do pacote devscripts do Debian em cada sharquivo do repositório para garantir que eu não introduza inadvertidamente sintaxe específica do Bash. No entanto, isso gera um erro para o meu .profile:

possible bashism in .profile line 51 ($BASH_SOMETHING):
if [ "${BASH_VERSION:-}" ]; then

Fiquei me perguntando se havia uma maneira de verificar qual shell está sendo executado que não acionaria um aviso checkbashisms.

Verifiquei a lista de variáveis ​​relacionadas ao shell listadas pelo POSIX na esperança de que uma delas pudesse ser usada para mostrar o shell atual. Também examinei as variáveis ​​definidas em um shell Dash interativo, mas, novamente, não consegui encontrar um candidato adequado.

No momento, excluí o .profileprocessamento por checkbashisms; Como é um arquivo pequeno, não é difícil verificá-lo manualmente. No entanto, depois de pesquisar o problema, eu ainda gostaria de saber se existe um método compatível com POSIX para determinar qual shell está sendo executado (ou pelo menos uma maneira que não causa checkbashismsfalha).


Antecedentes / esclarecimentos

Uma das razões pelas quais estou colocando meus arquivos de configuração do shell sob controle de versão é configurar meu ambiente em todos os sistemas nos quais atualmente efetuo logon regularmente: Cygwin, Ubuntu e CentOS (ambos 5 e 7, usando o Active Directory para usuários). autenticação). Costumo fazer logon através de ambientes X Windows / desktop e SSH para hosts remotos. No entanto, eu gostaria que isso fosse à prova do futuro e tivesse a menor dependência possível das dependências do sistema e de outras ferramentas.

Eu tenho usado checkbashismscomo uma verificação de integridade simples e automatizada para a sintaxe dos meus arquivos relacionados ao shell. Não é uma ferramenta perfeita, por exemplo, eu já apliquei um patch para que não reclame sobre o uso dos command -vmeus scripts. Enquanto pesquisava, aprendi que o objetivo real do programa é garantir a conformidade com a política Debian que, como eu a entendo, é baseada no POSIX 2004 em vez de 2008 (ou na revisão de 2013).

Anthony G - justiça para Monica
fonte
O que você está fazendo é tão compatível com POSIX quanto possível, quando o ponto principal é executar de maneira diferente em diferentes ambientes compatíveis com POSIX. Sua carne é com checkbashisms.
Gilles 'SO- stop be evil'
E, a propósito, nunca usei checkbashisms para verificar a portabilidade da minha configuração. Verifico usando-o em diferentes sistemas.
Gilles 'SO- stop be evil'
2
E, a propósito, para a coisa específica que você está fazendo aqui, escreva uma .bash_profileque use ambas .profilee (condicionalmente) .bashrc.
Gilles 'SO- stop be evil'
Obrigado pelo feedback @Gilles. Essa é uma solução muito elegante para o problema específico.
Anthony G - justiça para Monica

Respostas:

16

Seu

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

O código é totalmente compatível com POSIX e a melhor maneira de verificar se você está executando no momento bash. Claro que a $BASH_VERSIONvariável é específica do bash, mas é por isso que você a está usando! Para verificar se você está correndo bash!

Observe que $BASH_VERSIONserá definido se bashé chamado como bashou sh. Depois de afirmar que está sendo executado bash, você pode usar [ -o posix ]como um indicador que o shell foi chamado como sh(embora essa opção também seja definida quando POSIXLY_CORRECT estiver no ambiente ou bashfor chamado com -o posix, ou com SHELLOPTS = posix no ambiente. Mas em todos esses casos, bashse comportará como se fosse chamado como sh).


Outra variável que você pode usar em vez de $BASH_VERSIONe que checkbashismnão parece reclamar, a menos que seja aprovada a -xopção $BASH. Isso também é específico para bashum que você também deve poder usar para determinar se está executando bashou não.


Eu também argumentaria que não é realmente um uso adequado checkbashisms. checkbashismsé uma ferramenta para ajudá-lo a escrever shscripts portáteis (de acordo com a shespecificação da política Debian, um superconjunto do POSIX), ajuda a identificar a sintaxe não padrão introduzida por pessoas que escrevem scripts em sistemas nos quais shhá um link simbólico bash.

A .profileé interpretado por diferentes shells, muitos dos quais não são compatíveis com POSIX. Geralmente, você não usa shcomo seu shell de login, mas shell zsh, fishou bashcom recursos interativos mais avançados.

bashe zsh, quando não for chamado como she quando seu respectivo arquivo de sessão de perfil ( .bash_profile, .zprofile) não for compatível com POSIX (especialmente zsh), mas ainda for lido .profile.

Portanto, não é a sintaxe do POSIX desejada, .profilemas uma sintaxe compatível com o POSIX (for sh), bashe zshse você usar esses shells (possivelmente até o Bourne como o shell Bourne também lê, .profilemas não é comumente encontrado em sistemas baseados em Linux) )

checkbashismsdefinitivamente o ajudaria a descobrir os basismos, mas pode não apontar a sintaxe do POSIX que não é compatível com zshou bash.

Aqui, se você deseja usar um bashcódigo específico (como a solução alternativa desse bashbug, que não lê ~/.bashrcem shells de logon interativo), uma abordagem melhor seria ~/.bash_profilefazer isso (antes ou depois da busca ~/.profileonde você colocou sua sessão comum inicializações).

Stéphane Chazelas
fonte
Isso não passa checkbashismspor causa da variável usada.
22616 Thomas Thomas Dickey em
2
@ ThomasDickey, sim, mas é um caso em que o relatório de checkbashisms deve ser ignorado. Todas as outras soluções dadas até agora são consideravelmente piores.
Stéphane Chazelas
@ StéphaneChazelas Você diz que todas as outras soluções são piores. O que haveria de errado em usar $0para este caso em particular?
Anthony G - justice para Monica
2
@AnthonyGeoghegan Isso não faz distinção entre o bash chamado como she algum outro shell chamado como sh.
Gilles 'SO- stop be evil'
Ele também fornece um falso positivo se dashoutro shell não bash foi chamado incorretamente com argv[0] == "bash", o que é totalmente legal, se improvável.
Kevin
17

Geralmente se usa $0para esse fim. No site que você vinculou, está:

0
   (Zero.) Expands to the name of the shell or shell script.
jimmij
fonte
Tão simples! Eu me acostumei a usar $0o nome de um script de shell que esqueci que ele também pode se referir ao shell atual. Obrigado!
Anthony G - justice para Monica
11
Como de costume, essa convenção é respeitada por todos os programas bem comportados, mas um programa malicioso pode interferir com você usando várias chamadas de sistema da execfamília porque elas permitem que o programa de chamada identifique o binário a ser executado separadamente da cadeia a ser passada como argumento 0
dmckee 02/03
Isso funciona para o .profile :) bonito
Rob
5

Normalmente, a variável de ambiente SHELL informa o shell padrão. Você não precisa originar manualmente o arquivo .bashrc (não é verdade, veja a atualização abaixo), o bash deve fazer isso automaticamente, apenas verifique se ele está no diretório $ HOME.

A outra opção é fazer algo como:

 ps -o cmd= $$

que informará o comando (sem argumentos, o = sem valor não exibirá os cabeçalhos das colunas) do processo atual. Exemplo de saída:

 $ps -o cmd= $$
 bash
 $sh
 $ps -o cmd= $$
 sh

ATUALIZAR:

Eu estou corrigido! :)

O .bashrc nem sempre é originado como mencionado nos comentários abaixo e /programming/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

Assim, você pode mover seu .bashrc para .bash_profile e ver se isso funciona sem ter que fazer o teste. Caso contrário, você tem o teste acima.

Roubar
fonte
11
.bashrcnão é realmente originário de um shell de login, mas .profile(se existir) ou .bash_profileé. SHELLnão é especificado pelo POSIX e, infelizmente, também não é a cmdopção de formato ps.
Anthony G - justice para Monica
11
É isso que eu usaria, mas também está sujeito a manipulação maliciosa.
dmckee
Observe que, embora ps -o cmd= $$deva funcionar praticamente em todos os lugares, a $SHELLvariável é completamente irrelevante. Esse é o shell padrão do usuário e não tem influência sobre qual shell está sendo executado no momento ou qual shell está executando um script de shell.
terdon
2
Não, realmente não. muitos shells lerão ~/.profilequando iniciados como shells de login. Então, por exemplo, você $SHELLpoderia ser cshe você corre bash -l. Isso iniciará o bash como um shell de logon, e será lido ~/.profile, mas você $SHELLainda apontará para csh. Além disso, .profilepode ser fornecido explicitamente por outro script. Como o OP está buscando a máxima robustez, todos os tipos de casos devem ser considerados. De qualquer forma, $SHELLnão fornece nenhuma informação útil sobre o shell em execução no momento.
terdon
2
FWIW, eu também estou corrigido - cerca de SHELLnão ser especificado por POSIX: pubs.opengroup.org/onlinepubs/9699919799/basedefs/...
Anthony G - justiça para Monica
4

A pergunta pede o shell de login do usuário, bem como o shell atual , de uma maneira que agrade checkbashisms. Se esse for o shell no qual o usuário efetuou login, eu usaria o de /etc/passwd, por exemplo,

MY_UID=$(id -u)
MYSHELL=$(awk -F: '$3 == '$MY_UID'{ print $7; }' </etc/passwd )

Os usuários podem, é claro, iniciar um novo shell após efetuar o login. É claro que, se um é bash e o outro não, os testes das variáveis ​​de ambiente do bash podem não ajudar.

Alguns podem querer usar, getente não apenas o passwdarquivo (mas isso não estaria no escopo da pergunta).

Com base no comentário sobre LDAP e na sugestão de logname, este formulário alternativo pode ser usado:

MY_NAME=$(logname)
MYSHELL=$(getent passwd | awk -F: '$1 ~ /^'$MY_NAME'$/ {print $7;}' )

Ao testá-lo, notei que lognamenão gosta de sua entrada redirecionada (então deixei a expressão dividida). Uma verificação rápida mostra que getentdeve funcionar nas plataformas mencionadas (embora isso deva ter sido fornecido na pergunta original):

Thomas Dickey
fonte
11
Supondo que o banco de dados do usuário esteja em / etc / passwd (não LDAP / NIS / mysql ...) e que exista apenas um nome de usuário por userid. (seria melhor verificar a primeira coluna $(logname)).
Stéphane Chazelas
iirc, o POSIX não define o utilitário necessário (ou eu o teria usado na minha resposta). O uso idou uma variável modificável pelo usuário é uma escolha diferente.
Thomas Dickey
11
Note que eu disse $(logname), não $LOGNAME. O ID do usuário e o shell de login são derivados do nome de usuário usado no login. Você não pode voltar do ID do usuário para o shell de login, exceto nos sistemas em que há apenas um nome de usuário por ID de usuário. Ou IOW, a chave primária no banco de dados do usuário é o nome de usuário, não o uid.
Stéphane Chazelas
Obrigado @ThomasDickey por uma resposta que vem de uma perspectiva diferente. No entanto, é um pouco mais complexo do que eu estava pensando (mais como as respostas de jimmij e Stéphane ). Uma das razões pelas quais estou colocando meus arquivos de configuração do shell sob controle de versão é configurar meu ambiente em todos os sistemas nos quais atualmente efetuo logon regularmente: Cygwin, Ubuntu e CentOS (ambos 5 e 7, usando o Active Directory para usuários). autenticação). Por esse motivo, prefiro manter a lógica o mais simples possível.
Anthony G - justice para Monica