记忆

记忆

在里面bash 参考手册它指出:

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

因此我的问题是:何处/何时/如何可能

我尝试在我的设备上启用并测试该功能GNU bash,版本 4.4.12(1)-发布这样做:

shopts -s cmdhist
shopts -s lithist
echo -n "this is a test for a ";\
echo "multiline command"

然后我做了一个

history | tail 

期待一些类似于这样的输出:

101 shopts -s cmdlist
102 shopts -s lithist
103 echo -n "this is a test for a ";\
    echo "multiline command"
104 history

但得到这个:

101 shopts -s cmdlist
102 shopts -s lithist
103 echo -n "this is a test for a "; echo "multiline command"
104 history

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

答案1

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

记忆

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

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

$ echo -n "this is a test for a ";\
> echo "two line command"

当刚刚写入的行时,内存中存储了什么?

$ history 2
514  echo -n "this is a test for a ";echo "two line command"
515  history 2

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

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

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

$ echo " A test of 
> a new line"
A test of 
a new line

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

$ history 2
  518  echo "A test of
a new line"
  519  history 2

真正的多行命令:

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

$ for a in one two
> do echo "test $a"
> done
test one
test two

如果满足以下条件,则该历史记录将折叠为一行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     

每个命令的数字都发生了变化,因为在某个时刻我使用 .clear 清除了历史记录(在内存中)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

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

即使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

但如果两者都设置了:

$ 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

for命令存储为多行命令,并使用“换行符”而不是分号 ( ;)。与上面未设置 lithist 的情况进行比较。

磁盘

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

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

如果您希望将命令提交到磁盘,您需要有这套

export PROMPT_COMMAND='history -a'

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

现在,让我们开始讨论 cmdhist 和 lithist。事情并不像看起来那么简单。但别担心,一切很快就会清楚。

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

  • 使用默认值cmdhist(设置)和lithist(未设置)。
  • 同时设置
  • 两者均未设置
  • 使用未设置的 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

你将以这个结束(在记忆中):

$ 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

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

  • 编号为 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"

没有行号,只有命令,没有办法知道多行从哪里开始和结束。即使有多行也没有办法判断。

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

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

有没有解决方案,是的,使用额外的分隔符。
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"

现在您可以知道哪里以及哪条线是多线。

该格式'%FT%T '仅在使用历史命令时显示时间:

$ 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

答案2

这似乎不是一个多行命令(因为您逃避了返回)。如果你做的是有实际回报的事情,那就有区别了。

$ for i in /tmp/g*
> do echo $i
> done
/tmp/gigabyte
$ shopt -s lithist
$ for i in /tmp/g*
> do echo $i
> done
/tmp/gigabyte
$ history 
[...]
  517  for i in /tmp/g* ; do echo $i ; done
  518  shopt -s lithist
  519  for i in /tmp/g*
do echo $i
done
  520  history

答案3

iTerm完美地处理多行命令,它将多行命令保存为一个命令,然后我们可以使用++Cmd来导航历史记录。查看更多 iTerm 提示:Shift;使用 iTerm 有效工作

答案4

我试过shopt -s lithist cmdhist

但是注销和登录后,历史记录恢复~/.bash_history仍然分为多行:

 1953  2019-05-23 16:57:53 shopt -s cmdhist; shopt -s lithist
 1954  2019-05-23 16:57:58 for i in 1 2 3
 1955  2019-05-23 16:58:34 do echo $i
 1956  2019-05-23 16:58:34 done
 1957  2019-05-23 16:58:08 history 5
 1958  2019-05-23 16:58:27 history -a
 1959  2019-05-23 16:54:12 history 10 | less 

这种行为是预期的吗?

以下是我的内容~/.bash_history

#1558601873
shopt -s cmdhist; shopt -s lithist
#1558601878
for i in 1 2 3
do echo $i
done
#1558601888
history 5
#1558601907
history -a
#1558601652
history 10 | less 

解决方案

我通过删除旧的 ~/.bash_history 来解决这个问题,然后多行历史记录工作正常;即使在注销并登录后。

    1  2019-06-12 13:53:11 \rm .bash_history
    2  2019-06-12 13:53:27 fx mcd
    3  2019-06-12 13:53:28 mcd () 
{ 
    mkdir $@;
    cd $1
}
    4  2019-06-12 14:55:14 nmcli-restart-hotspot -s
    5  2019-06-12 21:34:35 ssh lab
    6  2019-06-12 23:38:45 history  | less 

相关内容