Como posso mostrar os resultados depois que o Bash lê uma linha completa de entrada, mas antes de executar qualquer um dos comandos nessa linha?

1

Do manual de bash, seção 6.6 alias ,

  …   Bash always reads at least one complete line of input before executing any of the commands on that line.  Aliases are expanded when a command is read, not when it is executed. Therefore, an alias definition appearing on the same line as another command does not take effect until the next line of input is read.

Estou tentando encontrar uma maneira de entender Qual das seguintes operações o shell executa quando" lê uma linha completa de entrada "e quando" executa algum dos comandos na linha "?

Existem alguns comandos e exemplos que podem mostrar os resultados

  • após Bash uma linha completa de entrada
  • mas antes de executar algum dos comandos nessa linha?

Por exemplo,

  • "uma linha completa de entrada" é um comando composto que consiste em vários comandos.
  • "uma linha completa de entrada" também pode ser um pipeline de vários comandos compostos, uma lista de pipelines, etc.
por Tim 04.08.2017 / 02:27

3 respostas

2

A parte do manual de Bash que você citou fala sobre

Aliases are expanded when a command is read, not when it is executed. Therefore, an alias definition appearing on the same line as another command does not take effect until the next line of input is read.

Então os aliases são realmente um daqueles exemplos que mostram a diferença entre ler e executar uma linha!

Para esclarecer, aqui está um comando composto. Primeiro, tentamos definir um alias e tentamos usá-lo:

alias echo=uname; echo

Alguém que não esteja familiarizado com o Bash esperaria que a saída desse comando seja Linux (ou qualquer sistema operacional que você esteja executando), mas na verdade ele não produz nada. Isso ocorre porque o Bash primeiro a linha e aplica definições de alias. Então é isso que o Bash vê:

alias echo=uname; echo

Observe que o segundo echo não é convertido em uname . Isso ocorre porque o Bash ainda não executou nosso comando alias echo=uname , ele apenas o leu . Depois que o Bash leu a linha, ele executa o que leu. Por isso, executa:

alias echo=uname; echo (que é exatamente o que lê, conforme descrito acima)

Podemos até checar se o Bash executou o comando digitando echo . Como definimos um alias antes, echo será convertido para uname durante a etapa de leitura, então o Bash executará uname .

Resumindo:

$ alias echo=uname; echo

$ echo
Linux

Editar:

Após a sua edição, você perguntou se há uma maneira de mostrar os resultados depois que o Bash lê uma linha de entrada, mas antes de executá-los. A resposta é sim, mas não é muito conveniente para o uso diário (no entanto, é certamente útil para entender o que está acontecendo dentro do shell).

Crie um arquivo com o seguinte conteúdo:

#!/bin/bash
shopt -s expand_aliases
trapped() {
    echo "On line ${1}, Bash sees: $BASH_COMMAND"
}
trap 'trapped ${LINENO}' DEBUG
alias echo=uname; echo
echo

Em seguida, execute esse arquivo.

Aqui está uma explicação de como esse script está funcionando:

  • shopt -s expand_aliases diz ao Bash para expandir aliases mesmo quando está no modo não interativo (como neste caso)
  • Em seguida, definimos uma função trapped() . Quando chamado, esta função irá imprimir o comando que é executado atualmente (junto com um número de linha, que é passado como parâmetro).
  • Chamamos trap 'trapped ${LINENO}' DEBUG . Isso diz ao Bash "sempre que você vir um sinal chamado DEBUG , execute trapped ${LINENO} first. DEBUG é um sinal gerado automaticamente pelo Bash antes de qualquer execução de um comando simples . Em outras palavras , antes que qualquer comando seja executado, nossa função trapped() é chamada.
  • Por fim, executamos alguns comandos para demonstração. No entanto, uma vez que temos uma armadilha, trapped() é executado antes de qualquer outra coisa acontecer, então o Bash imprime o que está prestes a ser executado. A variável LINENO é apenas um Bash embutido, que nos fornece o atual linenumber.

Se você quiser "executar" comandos sem executá-los e ainda ver o que o Bash lê, talvez você queira examinar a opção extdebug no manual do Bash.

    
por 04.08.2017 / 03:02
3

Uma boa maneira de entender isso seria dar uma olhada neste exemplo: na mesma linha">

Por exemplo, você quer executar o seguinte comando:

$ alias foo="echo bac" ; foo;
-bash: foo: command not found
$ foo
bac

Agora o bash lerá toda a linha e, como foo não está com alias, ela não será expandida (os aliases são expandidos durante a leitura). Agora, ao executar a linha, ele definirá o alias e lançará um erro.

    
por 04.08.2017 / 02:56
2

Sua pergunta não é clara, mas sua justaposição das frases

Aliases are expanded when a command is read, not when it is executed.

e

Bash always reads at least one complete line of input before executing any of the commands on that line.

sugere que o foco da sua pergunta seja a expansão de alias. (Eu digo "sugere" porque, além do trecho do manual de Bash, sua pergunta não menciona aliases.)

A versão original da sua pergunta é feita

How can I show that Bash always reads at least one complete line of input before executing any of the commands on that line?

A única maneira real de fazer isso é usar algum tipo de ferramenta de depuração / monitoramento / rastreamento para examinar o estado interno da casca. Isso é um pouco impraticável. A resposta do PawkyPenguin mostra uma abordagem para fazer um argumento de mão-ondulado para apoiar o que o manual diz. Aqui está uma variação de sua resposta que (IMO) é um pouco mais clara:

$ alias myalias=date

$ alias myalias="ls -ld"; myalias
Fri, Aug 04, 2017  3:33:33 AM

Podemos argumentar que o shell deve ter lido a (segunda) linha completa em sua totalidade antes de começar a executá-lo porque, quando executou myalias , executou como date . No momento em que o myalias alias foi executado, foi redefinido como ls -ld . Portanto, o shell deve ter expandido myalias para date antes de executar o

alias myalias="ls -ld"

comando, por isso deve ter lido a linha completa de entrada antes de executar qualquer um dos comandos nessa linha.

Sua pergunta revisada

How can I show the results after Bash reads one complete line of input but before executing any of the commands on that line?

faz um pouco mais de sentido neste contexto. Eu acho que você quer ver a linha de comando com os alias expandidos antes de executar qualquer um dos comandos nessa linha; Ou seja, veja os resultados da expansão de alias. Eu acho que você quer ver algo como

alias myalias="ls -ld"; date

Até onde eu sei, não há como obter essa saída - a linha completa com aliases expandida. Mas a opção xtrace ( -x ) chega perto.

$ set -x

$ alias myalias=date
+ alias myalias=date

$ alias myalias="ls -ld"; myalias
+ alias 'myalias=ls -ld'
+ date                                                         ← The expansion of the alias
Fri, Aug 04, 2017  3:33:42 AM

Claro, isso gera cada comando expandido apenas pouco antes de ser executado, então os resultados da expansão de alias são misturados com a saída dos comandos. Além disso, isso mostra todas expansões:

$ animal=cat
+ animal=cat

$ echo Hello; echo world; myalias "$animal" R*
+ echo Hello
Hello
+ echo world
world
+ ls -ld cat README                                            ← The expansion of the alias
ls: cannot access cat: No such file or directory
-rw-r--r-- 2 myusername  mygroupname  489 Feb 22  2015 README

Não conseguimos ver ls -ld "$animal" R* ; nós o vemos somente após expansão de expansão e expansão de nome de caminho (glob). Também não “joga bem” com pipelines:

$ alias myalias=date
+ alias myalias=date

$ myalias | od -cb
+ date                                                         ← The expansion of the alias
+ od -cb
0000000   F   r   i   ,       A   u   g       0   4   ,       2   0   1
        106 162 151 054 040 101 165 147 040 060 064 054 040 062 060 061
0000020   7           9   :   4   2   :   1   7       P   M  \n
        067 040 040 071 072 064 062 072 061 067 040 120 115 012
0000036

em que a saída de rastreio nos mostra date e od -cb em duas linhas separadas. (Você precisaria olhar para a linha de comando original para descobrir que estava fazendo date | od -cb , se não fosse óbvio na saída.)

    
por 04.08.2017 / 05:50