Como obter o nome do shell ancestral interativo mais próximo de um script?

2

Eu sei que alguém pode usar o $- contém i ou não, ou verificar se $PS1 está vazio para dizer se um shell é interativo.

No entanto, essas soluções funcionam apenas para o shell atual.

Eu tenho um script bash tentando encontrar o shell pai imediato que é interativo.

Por exemplo:

  1. shell interativo: zsh
  2. bash script 1, que executa o script bash 2
  3. bash script 2 contém o mecanismo para descobrir o shell interativo imediato

Então, quando executamos o script bash 1 em nosso shell interativo zsh , esperamos uma saída de zsh .

Não consigo descobrir como posso fazer isso, quando o script está sendo executado no sub-shell.

Nota

Eu quero que o script seja executado, não originado.

Esclarecer

I have a bash script trying to find the first ancestor shell that is interactive.

Por primeiro ancestral, eu quis dizer que o primeiro shell interativo que encontramos durante o processo de varredura de cadeia de processo bottom-up.

Por exemplo, no caso de: zsh (primeiro shell interativo) - > bash (segundo shell interativo) - > bash (shell em lote para o script 1) - > bash (shell em lote para o script 2), queremos produzir o bash (o segundo shell interativo).

    
por GJ. 25.07.2015 / 05:11

2 respostas

3

Este é um requisito muito estranho. Por que você se importaria com o que o shell interativo invocava seu script, ou invocava algum outro programa que não fosse um shell interativo que, por sua vez, invocava seu script? Isso tem um cheiro muito strong de um problema XY .

Se você realmente precisa saber, pode tentar descobrir, mas não acho que exista um método totalmente confiável, apenas um que funcione em casos típicos.

A partir de $PPID , rastreie os processos ancestrais de seu script ( ps -o ppid= -p $ancestor_pid ) até encontrar o que você está procurando ou um que indique que você foi longe demais.

Uma estratégia simples é procurar um processo em um grupo de processos diferente ( ps -o pgid= -p $ancestor_pid ). Em circunstâncias normais, se o seu script foi invocado por (um script que foi invocado por) um shell interativo, o processo que você alcançou é um shell com controle de tarefa, que executou seu script (script pai) em um grupo de processos separado .

Aqui estão alguns exemplos de coisas que podem dar errado com essa estratégia:

  • Um dos processos dessa cadeia já está morto.
  • Seu script não foi chamado por meio de um shell interativo, mas por meio de um cron job, de um programa X11, etc.

Você pode ou não querer verificar se a entrada padrão, a saída padrão e o erro padrão deste processo (por exemplo, com lsof ou via /proc se você não precisa de portabilidade além do Linux) são o mesmo terminal do script . Depende de como você quer lidar com casos como

bash$ xterm -e your_script
    
por 26.07.2015 / 00:17
2

Você verifica suas opções.

[ "$-" = "${-#*i}" ] ||
echo shell is interactive

Você também pode verificar seus descritores de arquivo. Isto é um pouco diferente. Ele não lhe dirá necessariamente se o shell é interativo por si só, mas dirá se está falando com um terminal.

for fd in 0 1 2
do     [ -t "$fd" ] && 
       break 
done|| echo shell fds 0 1 2 are not connected to a terminal.

Podemos hackear isso um pouco na tentativa de descobrir todos os processos de uso do nosso terminal.

tty_users()
    for fd in 0 1 2 "$@"
    do     [ -t "$fd" ] && {
           fuser "$(tty)"
           break; } <&"$fd"
    done 

Que será executado no shell atual e imprimirá uma lista de ids de processo para os processos em execução no primeiro terminal detectado como associado a std(in|out|err) (por padrão - passar a função numérica argumentos para testar outros) , respectivamente. Ele configura o shell var $fd para o número do descritor de arquivo associado a esse terminal e retorna false se nenhum dos descritores padrão ou nenhum argumento estiver associado a um terminal.

O acima é provavelmente o mais próximo possível, a menos que você procure pelo ID da sessão do seu terminal - se tiver um.

ps -osid= -p"$$"

Isso retornará para você o pid do proprietário do seu terminal de controle - se você tiver um.

Para demonstrar:

echo "$$"; sh -c 'sh -c "ps -osid= -p\"\$$\""' 
6023
6023

Mas você não pode confiar em nada disso. Não é realmente nada. Olhe:

sh -acm 'IFS=\; i=0;eval "$0"'          \
        '[ "$i" -lt 5 ] && eval "$*"'   \
        'ps -opid -opgid -p"$$"'        \
        'sh -acm "$0" "$0" "$@" i=$((i+=1))'
  PID  PGID
28766 28766
  PID  PGID
28768 28768
  PID  PGID
28770 28770
  PID  PGID
28772 28772
  PID  PGID
28774 28774

Você vê? O shell interativo a partir do qual eu corro esses processos não está nem nessa lista. Cada sh lança um filho próprio até atingir uma profundidade de 5, e como cada um chama ps para imprimir seu PID e seu PGID. O processo Every recebe um novo PGID. O controle de tarefas é tão relacionado a um shell interativo quanto um terminal, é apenas que o terminal é a maneira mais direta de testá-lo.

    
por 25.07.2015 / 06:44