web-dev-qa-db-ja.com

bashでの複数行の履歴エントリ(別名リシスト)はいつ可能ですか?

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"

それから私は

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が不可能だったのはなぜですか?私は何を間違えましたか?

8

\<new line>は、履歴で<new line>を取得する正しい方法ではありません。

記憶

履歴行はシェルメモリ(ディスクではない)に保持されているため、履歴行のみを処理します。

あなたがしたように、いくつかのコマンドを入力しましょう:

$ 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

真の複数行コマンド:

複数行コマンドの考えられる例の1つは、次のとおりです。

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

cmdhist is setの場合、これは1つの履歴行に折りたたまれます。

$ 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     

ある時点で(メモリ内の)履歴を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が設定されていない上記と比較してください。

ディスク

上記のすべては、シェルのメモリに保持されているコマンドのリストを使用して説明されました。コマンドはディスクに書き込まれませんでした。 (デフォルト)ファイル~/.bash_historyは変更されていません。

そのファイルは、実行中のシェルが終了すると変更されます。その時点で、履歴はファイルを上書きするか(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

3つの複数行コマンドは次のように終了します。

  • 2行目に1つ(1行、1コマンド)。
  • 4番の複数行に1つ(複数行に1つのコマンド)
  • 6から8までの番号が付けられた数行に1つ

わかりました、しかしファイルで何が起こりますか?すでに言ってください..。

最後に、ファイル内:

簡単です。ファイルに書き込んで、これを確認してください。

$ 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"

行番号はなく、コマンドのみです。複数行がどこから始まりどこで終わるかを知る方法はありません。複数行があっても見分ける方法はありません。

実際、それがまさに起こることです。コマンドが上記のようにファイルに書き込まれた場合、ファイルが読み戻されると、複数行に関する情報が失われます。

区切り文字(改行)は1つだけで、各行は1つのコマンドとして読み戻されます。

これに対する解決策はありますか、はい、追加の区切り文字を使用します。
HISTTIMEFORMATのようなものがそれを行います。

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コマンドを使用する場合のみです。

$ 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
10
Isaac

これは複数行のコマンドではないようです(リターンをエスケープしたため)。あなたが本当のリターンでそれをするならば、違いがあります。

$ 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
BowlOfRed

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 
0
gholk

iTermは複数行のコマンドを完全に処理し、複数行のコマンドを1つのコマンドとして保存してから、次のコマンドを使用できます。 Cmd+Shift+; 履歴をナビゲートします。 iTermを効果的に使用する でiTermのヒントをさらに確認してください。

0
jeffery.yuan

継続行(行末の\)は、何があっても削除されているようです。継続を行うには2つの代替方法があることに気づきました。

  1. バッククォート。コマンド置換。 私は後者よりもこれを好む

    $ echo "1st line";`
    > `echo "2nd line"
    1st line
    2nd line
    
    $ history 2
      507  echo "1st line";`
    `echo "2nd line"
      508  history 2
    
  2. ドル記号。まだコマンド置換を悪用しています。

    $ echo "1st line";$(
    > ) echo "2nd line"
    1st line
    2nd line
    
    $ history 2
      501  echo "1st line";$(
    ) echo "2nd line"
      502  history 2
    

ええあまりない魅力的。考えてみれば、EOLのスラッシュがきれいだと思ったのはいつですか?

当初は:を含めることを考えていましたが、表示されているように、置換はそれなしで機能するので、それを使用します。

すばらしいのは、shopt -u lithistでも機能することです。 ~/.bash_historyから履歴をロードする新しいbashセッションを開始しても、それらは切断されます。

0
Hincor