bash 中的多行历史条目(又名 lithist)何时可能?

hum*_*ace 10 bash command-history shopt

bash 参考手册中,它指出:

lithist 如果启用并启用该cmdhist选项,多行命令将使用嵌入的换行符保存到历史记录中,而不是在可能的情况下使用分号分隔符。

因此我的问题是:这在哪里/何时/如何可能

我尝试在我的GNU bash 版本 4.4.12(1)-release上启用和测试该功能这样做:

shopts -s cmdhist
shopts -s lithist
echo -n "this is a test for a ";\
echo "multiline command"
Run Code Online (Sandbox Code Playgroud)

然后我做了一个

history | tail 
Run Code Online (Sandbox Code Playgroud)

期待一些类似于此的输出:

101 shopts -s cmdlist
102 shopts -s lithist
103 echo -n "this is a test for a ";\
    echo "multiline command"
104 history
Run Code Online (Sandbox Code Playgroud)

而是得到这个:

101 shopts -s cmdlist
102 shopts -s lithist
103 echo -n "this is a test for a "; echo "multiline command"
104 history
Run Code Online (Sandbox Code Playgroud)

很明显,多行命令(bash 历史记录编号为 103 的命令)没有用“嵌入换行符而不是分号分隔符”存储。为什么lithist这里不可能?我做错了什么?

ImH*_*ere 15

A\<new line>不是<new line>在历史记录中获得 a 的正确方法。

记忆

让我们只处理历史行,因为它们保存在 shell 内存(而不是磁盘)中。

让我们像您一样输入几个命令:

$ echo -n "this is a test for a ";\
> echo "two line command"
Run Code Online (Sandbox Code Playgroud)

刚刚写的那行,内存中存储了什么?

$ history 2
514  echo -n "this is a test for a ";echo "two line command"
515  history 2
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,“行延续”,一个反斜杠后跟一个换行符,被删除了。
正如它应该的(来自 man bash):

如果出现 \ 对,并且反斜杠本身没有被引用,则 \ 被视为行继续(即,它从输入流中删除并有效地被忽略)。

如果我们引用它,我们可能会得到一个换行符:

$ echo " A test of 
> a new line"
A test of 
a new line
Run Code Online (Sandbox Code Playgroud)

并且,在这一点上,历史将反映:

$ history 2
  518  echo "A test of
a new line"
  519  history 2
Run Code Online (Sandbox Code Playgroud)

一个真正的多行命令:

多行命令的一个可能示例是:

$ for a in one two
> do echo "test $a"
> done
test one
test two
Run Code Online (Sandbox Code Playgroud)

如果出现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     
Run Code Online (Sandbox Code Playgroud)

每个命令的数字都发生了变化,因为在某些时候我用history -c.

如果您取消设置 cmdhist,您将得到以下信息:

$ 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
Run Code Online (Sandbox Code Playgroud)

每行(不是完整的命令)将在历史记录中的单独一行中。

即使lithist设置了:

$ 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
Run Code Online (Sandbox Code Playgroud)

但如果两者都设置:

$ 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
Run Code Online (Sandbox Code Playgroud)

for命令存储为带有“换行符”而不是分号 ( ;)的多行命令。与上面没有设置 lithist 的地方进行比较。

磁盘

以上所有内容都是使用保存在 shell 内存中的命令列表来解释的。没有命令写入磁盘。(默认)文件~/.bash_history未更改。

当正在运行的 shell 退出时,该文件将被更改。届时,历史记录将覆盖文件(如果histappend未设置),或者以其他方式附加。

如果要将命令提交到磁盘,则需要进行以下设置

export PROMPT_COMMAND='history -a'
Run Code Online (Sandbox Code Playgroud)

这将使每个命令行都附加到每个新命令行上的文件中。

现在,让我们开始讨论 cmdhist 和 lithist。这并不像看起来那么简单。不过别担心,一会一切都会清楚的。

假设您花时间键入下面的所有命令(没有快捷方式,没有别名,没有功能,您需要实际的命令,抱歉)。
首先清除内存 ( history -c) 和磁盘中的所有历史记录(进行备份) ( history -w),然后尝试 3 次:

  • 使用 cmdhist (set) 和 lithist (unset) 的默认值。
  • 两套
  • 两者都未设置
  • 使用未设置的 cmdhist 设置 lithist 没有任何意义(您可以对其进行测试)。

要执行的命令列表:

$ 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
Run Code Online (Sandbox Code Playgroud)

你将以这个结束(在内存中):

$ 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
Run Code Online (Sandbox Code Playgroud)

三个多行命令的结尾如下:

  • 一行编号为 2(一行,一个命令)。
  • 编号为 4 的多行中的一个(多行中的一个命令)
  • 编号从 6 到 8 的几行之一

好的,但是文件中发生了什么?说已经....

最后,在文件中:

很简单,写入文件并使用 cat 来查看:

$ 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"
Run Code Online (Sandbox Code Playgroud)

没有行号,只有命令,没有办法告诉多行从哪里开始和在哪里结束。即使有多行也无法分辨。

事实上,这正是发生的情况,如果命令如上写入文件,当文件被读回时,有关多行的任何信息都会丢失。

只有一个分隔符(换行符),每一行都作为一个命令读回。

有没有办法解决这个问题,是的,使用额外的分隔符。
HISTTIMEFORMAT 就是这样做的。

历史时间格式

当此变量设置为某个值时,执行每个命令的时间将作为注释 ( #) 字符后的纪元以来的秒数(是的,始终为秒)存储在文件中。

如果我们设置变量并重新写入~/.bash_history文件,我们会得到:

$ 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"
Run Code Online (Sandbox Code Playgroud)

现在您可以分辨出哪里和哪条线是多线。

格式'%FT%T '显示时间,但仅在使用 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
Run Code Online (Sandbox Code Playgroud)