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 次:
要执行的命令列表:
$ 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)
三个多行命令的结尾如下:
好的,但是文件中发生了什么?说已经....
很简单,写入文件并使用 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)