Por que a sequência importa na execução desses comandos bash?

10

Parece haver alguma inconsistência que não consigo entender sobre o shell bash.

Se eu executar:

ls;date;time

os resultados das três consultas são mostrados em sequência.

No entanto, na troca de posição de data e hora, uma mensagem de erro é exibida.

Então, se eu executar:

ls;time;date

a mensagem de erro diz: bash: syntax error near unexpected token 'date'.

Alguém pode explicar isso?

rohitvijaysharma
fonte
Suas mentiras problema Em time;datevs date;time. Isso parece ser um problema com o pipeline bashe o último caractere gerado com a timesaída. Os resultados testados em diferentes emuladores de terminal são: - [Bash] $ date; time # [OK] $ time; date # [ NotOK ] bash: erro de sintaxe próximo ao token inesperado `date '$ time # apenas o erro não parece ser o resultado de qualquer data. - [Csh] $ data; hora # [OK] $ hora; data # [OK] - [Tcsh] $ data; hora # [OK] $ hora; data # [OK] - [Ksh] $ data; hora # [ OK] $ time; date # [OK]
Mostafa Shahverdy
Atualizei minha resposta com uma explicação para a mensagem de erro. Verifique se esta é a resposta que você estava procurando.
Zwets

Respostas:

10

O timecomando no seu pipeline não é o /usr/bin/timebinário, mas o bash timeembutido. Compare man timecom help time. O erro que você vê é o bash falhando ao analisar timeo argumento. Isso deve estar presente ou ser uma nova linha. É uma nova linha no seu primeiro exemplo, mas ausente no segundo.

Por outro lado, se você fosse correr

ls;date;'time'

ou

ls;'time';date

onde as aspas ao redor 'time'revogam seu status como uma palavra reservada, o bash não tem problemas ao analisar a linha. Agora ele analisa três comandos em uma lista, os quais serão executados em sequência e, /usr/bin/timeem ambos os casos , reportará um erro de uso.

Termo aditivo

Observou-se que, embora time ; dateproduza um erro, time ; ; datenão. A explicação provável é que time ;é interpretado pelo bash como equivalente a time <newline>. A expressão time ; ; dateé então analisada como a lista de time ;e date.

Isso é consistente com a observação de que time ;e também time ; ;é legal, sendo o segundo analisado como a lista de singleton contendo time ;seguida pelo ponto-e-vírgula opcional permitido após as listas.

Portanto, outra maneira de explicar por que time ; dateo erro bash: syntax error near unexpected token 'date'ocorre é que timeconsome o ponto e vírgula que o separa date. Só pode fazer isso porque timeé uma palavra reservada do bash.

zwets
fonte
Obrigado por uma boa explicação! Mas, novamente, esse comportamento parece um bug para mim: timedeve permitir um comando NULL e o ponto e vírgula deve delimitar as listas, portanto, o IMO timenão deve "consumir" o ponto e vírgula após ele. Outros comandos internos (que podem receber argumentos) não exibem esse tipo de comportamento.
organize
@arrange A complicação é que o tempo não permite um comando nulo (isso teria desambiguado tudo), apenas permite uma nova linha no lugar do comando. Então, de time;datefato, está sintaticamente errado em qualquer interpretação. No entanto, time ; e time ; ;também seria ilegal. Pode-se debater se timeo comportamento é um bug ou apenas não documentado ( é consistente internamente), mas um relatório de bug definitivamente estaria em vigor. Você gostaria de arquivá-lo?
Zwets
Bem, eu olhei para a fonte (bash4.2: parse.y: linhas 1205-1221) e lá diz isso time by itself can time a null commande depois o faz $$ = make_simple_command (x, (COMMAND *)NULL);. Quanto a registrar um bug, não tenho certeza. 8)
organize
Deve-se notar que esse problema é específico do bash. Fazendo time ; dateobras em ksh93e mkshsem erros, mesmo que em kshtimepalavras-chave.
perfil completo de Sergiy Kolodyazhnyy
2

O Bash trata o interno timecomo um caso especial ao analisar linhas de comando.

Como pode ser lido na página de manual do bash, a linha digitada é primeiro dividida em uma lista:

pipeline ; pipeline

onde um pipeline é:

[time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]

ou no nosso caso, simplesmente:

time command

ou seja, se o tempo estiver presente, o comando também deverá estar presente.

[Há um caso especial que permite timeser seguido por uma nova linha, mas que não se aplica aqui]

Então, no nosso caso, temos:

time;date

sendo dividido em dois pipelines:

1. time
2. date

e o pipeline 1 não está bem formado, pois temos timesem um comando. Daí o erro.

Observe que a linha de comando timetambém não funciona aqui:

$ /usr/bin/time;date
Usage: /usr/bin/time [-apvV] [-f format] [-o file] [--append] [--verbose]

bash analisa isso conforme o esperado, em 2 pipelines:

1. /usr/bin/time
2. date

e /usr/bin/timedepois se recusa a executar sem nenhum argumento. Observe que este é um erro e /usr/bin/timenão um erro do bash.

O motivo pelo qual o back-tick funciona é que o back-tick deixa de timeser interpretado como um elemento especial dentro do pipeline.

ou seja, com o back-tick:

`time`;date

é analisado como dois pipelines:

1. `time`
2. date

Lembre-se de que um pipeline, no nosso caso, é:

[time] command

e o problema inicialmente era que tínhamos timesem comando, o que não é permitido. Mas agora simplesmente temos o comando:

`time`

sem o anterior time, pois os back-ticks significam que timeé interpretado como o comando, não como uma palavra anterior.

Então, o bash executa seu built time-in sem argumentos, o que é aceito. Não produz saída e não vemos erro.

Observe que:

`time`

realmente executa o resultado do timebuilt-in, ou seja, executa o que o timebuilt-in produz no stdout. Mas como, timepor si só, não escreve nada para o stdout, parece funcionar.

Por fim, observou-se que isso funciona:

time ; ; date

o que não posso explicar, infelizmente :)

cdmackay
fonte
Acho que sua explicação é melhor, mas ainda me parece estranha. ;datebash: syntax error near unexpected token ;, mas time ;datebash: syntax error near unexpected token date, então parece que o bash não trata o comando após o tempo incorporado como "; data". Curiosamente, time ; ; datefunciona.
organize
sim, obrigado @arrange, é bastante estranho. Vou atualizar a resposta um pouco.
cdmackay
ok, @arrange, reescrito. Ainda não consigo explicar o seu último ... suspiro.
cdmackay
@cdmackay Você está misturando backticks e aspas. Ao citar 'time' , perde seu significado como uma palavra reservada. Fazendo backticking, ele é executado em um subshell cuja saída é unida ao comando. Isso não tem nada a ver com a discussão. De fato, seu exemplo `time\';dateprova o contrário de sua afirmação: isso deve gerar um erro no seu raciocínio, pois /usr/bin/timerequer um argumento. A razão pela qual isso não ocorre é porque, no subshell em que é executado, é a palavra reservada timemais uma vez.
Zwets
@arrange Ambos são erros de sintaxe e ambos são relatados como estando perto do mesmo local, por isso não vejo uma inconsistência lá. Depois que ele entra na área de erro de sintaxe, você não pode esperar que um analisador saiba sua saída. Se você precisar de um analisador, ele deve conhecer não apenas a sintaxe legal, mas também a sintaxe de toda construção ilegal possível, o que é impossível por definição.
Zwets