Um \<new line>
não é a maneira correta de obter um <new line>
no histórico.
Memória
Vamos lidar apenas com as linhas do histórico, pois elas são mantidas na memória shell (não no disco).
Permite digitar alguns comandos como você fez:
$ echo -n "this is a test for a ";\
> echo "two line command"
O que foi armazenado na memória como a linha que acabou de ser escrita?
$ history 2
514 echo -n "this is a test for a ";echo "two line command"
515 history 2
Como você pode ver, a "continuação de linha", uma barra invertida seguida por uma nova linha, foi removida.
Como deveria (do homem bash):
If a \ pair appears, and the backslash is not itself quoted, the \ is treated as a line continuation (that is, it is removed from the input stream and effectively ignored).
Podemos receber uma nova linha se citá-la:
$ echo " A test of
> a new line"
A test of
a new line
E, nesse ponto, a história refletirá isso:
$ history 2
518 echo "A test of
a new line"
519 history 2
Um verdadeiro comando de várias linhas:
Um exemplo possível de um comando com várias linhas é:
$ for a in one two
> do echo "test $a"
> done
test one
test two
Que será reduzido em uma linha do histórico se cmdhist is set
:
$ shopt -p cmdhist lithist
shopt -s cmdhist
shopt -u lithist
$ history 3
24 for a in one two; do echo "test $a"; done
25 shopt -p cmdhist lithist
26 history 3
Os números de cada comando mudaram porque em algum momento eu limpei o histórico (na memória) com um history -c
.
Se você não selecionar o cmdhist, você verá isto:
$ shopt -u cmdhist
$ for a in one two
> do echo "test $a"
> done
test one
test two
$ history 5
5 shopt -u cmdhist
6 for a in one two
7 do echo "test $a"
8 done
9 history 5
Cada linha (não um comando completo) estará em uma linha separada no histórico.
Mesmo se o lithist
estiver definido:
$ shopt -s lithist
$ for a in one two
> do echo "test $a"
> done
test one
test two
$ history 5
12 shopt -s lithist
13 for a in one two
14 do echo "test $a"
15 done
16 history 5
Mas se ambos estiverem definidos:
$ shopt -s cmdhist lithist
$ for a in one two
> do echo "test $a"
> done
$ history 5
23 history 15
24 shopt -p cmdhist lithist
25 shopt -s cmdhist lithist
26 for a in one two
do echo "test $a"
done
27 history 5
O comando for
foi armazenado como um comando multilinha com "novas linhas" em vez de ponto e vírgula ( ;
). Compare com acima onde o lithist não foi definido.
Disco
Todos os itens acima foram explicados usando a lista de comandos mantidos na memória do shell. Nenhum comando foi gravado no disco. O arquivo (padrão) ~/.bash_history
não foi alterado.
Esse arquivo será alterado quando o shell em execução for encerrado. Nesse momento, o histórico sobrescreverá o arquivo (se histappend
não estiver definido) ou será anexado de outra forma.
Se você quer que os comandos sejam enviados para o disco, você precisa tenha esse conjunto :
export PROMPT_COMMAND='history -a'
Isso fará com que cada linha de comando seja anexada ao arquivo em cada nova linha de comando.
Agora, vamos ao que interessa: cmdhist e lithist. Não é tão simples quanto parece. Mas não se preocupe, tudo ficará claro em um momento.
Digamos que você tenha tempo para digitar todos os comandos abaixo (não há atalho, sem alias, sem função, você precisa dos comandos reais, desculpe).
Para limpar primeiro todo o histórico na memória ( history -c
) e no disco ( faça um backup ) ( history -w
) e tente três vezes:
- Com os valores padrão de cmdhist (set) e lithist (unset).
- com o conjunto
- Com ambos, não definidos
- Configurar o lithist com um cmdhist não definido não faz sentido (você pode testá-lo).
Lista de comandos para executar:
$ history -c ; history -w # Clear all history ( Please backup).
$ shopt -s cmdhist; shopt -u lithist
$ for a in one two
> do echo "test $a"
> done
$ shopt -s cmdhist; shopt -s lithist
$ for a in one two
> do echo "test $a"
> done
$ shopt -u cmdhist; shopt -u lithist
$ for a in one two
> do echo "test $a"
> done
Você terminará com isso (na memória):
$ history
1 shopt -s cmdhist; shopt -u lithist
2 for a in one two; do echo "test $a"; done
3 shopt -s cmdhist; shopt -s lithist
4 for a in one two
do echo "test $a"
done
5 shopt -u cmdhist; shopt -u lithist
6 for a in one two
7 do echo "test $a"
8 done
9 history
Os três comandos multilinha terminam da seguinte forma:
- um na linha numerada 2 (uma linha única, um comando).
- um em uma multilinha numerada 4 (um comando em várias linhas)
- um em várias linhas numeradas de 6 a 8
Ok, mas o que acontece no arquivo? diga isso ...
finalmente, no arquivo:
Simples, escreva no arquivo e gata para ver isso:
$ history -w ; cat "$HISTFILE"
shopt -s cmdhist; shopt -u lithist
for a in one two; do echo "test $a"; done
shopt -s cmdhist; shopt -s lithist
for a in one two
do echo "test $a"
done
shopt -u cmdhist; shopt -u lithist
for a in one two
do echo "test $a"
done
history
history -w ; cat "$HISTFILE"
Não há números de linha, apenas comandos, não é possível saber onde uma multilinha é iniciada e onde ela termina. Não há como saber se existe uma multilinha.
Na verdade, é exatamente isso que acontece, se os comandos forem gravados no arquivo acima, quando o arquivo for lido novamente, qualquer informação sobre multilinhas será perdida.
Existe apenas um delimitador (a nova linha), cada linha é lida de volta como um comando.
Existe uma solução para isso, sim, para usar um delimitador adicional.
O tipo HISTTIMEFORMAT faz isso.
HISTTIMEFORMAT
Quando esta variável é definida para algum valor, a hora em que cada comando foi executado é armazenada no arquivo como os segundos desde epoch (sim, sempre segundos) após um caractere de comentário ( #
).
Se definirmos a variável e reescrevermos o arquivo ~/.bash_history
, obteremos isso:
$ HISTTIMEFORMAT='%F'
$ history -w ; cat "$HISTFILE"
#1490321397
shopt -s cmdhist; shopt -u lithist
#1490321397
for a in one two; do echo "test $a"; done
#1490321406
shopt -s cmdhist; shopt -s lithist
#1490321406
for a in one two
do echo "test $a"
done
#1490321418
shopt -u cmdhist; shopt -u lithist
#1490321418
for a in one two
#1490321418
do echo "test $a"
#1490321420
done
#1490321429
history
#1490321439
history -w ; cat "$HISTFILE"
#1490321530
HISTTIMEFORMAT='%FT%T '
#1490321571
history -w ; cat "$HISTFILE"
Agora você pode dizer onde e qual linha é uma multilinha.
O formato '%FT%T '
mostra a hora, mas apenas ao usar o comando history:
$ history
1 2017-03-23T22:09:57 shopt -s cmdhist; shopt -u lithist
2 2017-03-23T22:09:57 for a in one two; do echo "test $a"; done
3 2017-03-23T22:10:06 shopt -s cmdhist; shopt -s lithist
4 2017-03-23T22:10:06 for a in one two
do echo "test $a"
done
5 2017-03-23T22:10:18 shopt -u cmdhist; shopt -u lithist
6 2017-03-23T22:10:18 for a in one two
7 2017-03-23T22:10:18 do echo "test $a"
8 2017-03-23T22:10:20 done
9 2017-03-23T22:10:29 history
10 2017-03-23T22:10:39 history -w ; cat "$HISTFILE"
11 2017-03-23T22:12:10 HISTTIMEFORMAT='%F'
12 2017-03-23T22:12:51 history -w ; cat "$HISTFILE"
13 2017-03-23T22:15:30 history
14 2017-03-23T22:16:29 HISTTIMEFORMAT='%FT%T'
15 2017-03-23T22:16:31 history
16 2017-03-23T22:16:35 HISTTIMEFORMAT='%FT%T '
17 2017-03-23T22:16:37 history