bashで出力の最後の行を実行するにはどうすればよいですか?


12

私は、bashで最後のコマンドを実行できることを知って!!いますが、出力の最後の行を実行するにはどうすればよいですか?

私はこの出力のユースケースを考えています:

The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

しかし、私はそれを実行する方法を知りません。私は!!、多分@@または類似のようなものを考えていますか?


スーパーユーザーにもこの質問があります。


1
コピーして貼り付けてみませんか?
パイロット

1
@Timプログラムの出力の最後の行を実行する方法が必要ですが、この特定のユースケースには、command_not_found_handleシェル関数または実行するスクリプト(通常は単に/usr/lib/command-not-found)を変更するアプローチもあります。パッケージを自動的にインストールするオプションを使用して対話形式でプロンプトが表示されるようにするか、(おそらくより良い)パッケージの名前がどこかに保存され、短いコマンドを実行してインストールできるようにすることができると思います。これはここで尋ねたものとは十分に異なります。それについて別の質問を投稿することを検討してください。
エリアケイガン


2
関連する場合もあります:github.com/nvbn/thefuck(名前を無視)このツールはgit / apt / etcコマンドを自動修正します:)
bhathiya-perera

回答:


16

コマンド$(!! |& tail -1)は以下を行う必要があります。

$ git something
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

$ $(!! |& tail -1)
$(git something |& tail -1)
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
git-man liberror-perl

ご覧のとおり、sudo apt-get install gitコマンドが実行されています。

編集:内訳$(!! |& tail -1)

  • $()あるbashコマンド置換パターンは、

  • bash!!最後に実行されたコマンドに展開されます

  • |&一部はトリッキーです。通常、パイプ|は左側のコマンドのSTDOUTを取得し、それをSTDINとして右側のコマンドに渡し|ます。この場合、前のコマンドはエラーメッセージとしてSTDERRに出力を出力します。したがって、実行して|も役に立たないので、STDOUTとSTDERRの両方(またはSTDERRのみ)を右側のコマンドに渡す必要があります。|&STDOUTおよびSTDERRの両方をSTDINとして右側のコマンドに渡します。または、STDERRのみを渡すことができます。

    $(!! 2>&1 >/dev/null | tail -1)
  • tail -1通常どおり、入力から最後の行を出力します。ここでは、最後の行を印刷するのではなく、apt-get installコマンドを含む行を印刷するように、より正確にすることができます。

    $(!! 2>&1 >/dev/null | grep 'apt-get install')

1
アプローチは、出力が2回目に同一になることを前提としています。一部のコマンドは、次回に別の出力を生成できます。その場合、出力の最終行を実際に実行することを予測することは非常に困難です。
カスペルド

1
@kasperdあなたは正しい..この特定の例に関する限り、出力は同じままである必要があります。
heemayl

7

TL; DR: alias @@='$($(fc -ln -1) |& tail -1)'

Bashの履歴相互作用機能は、コマンドの出力を調べるメカニズムを提供しません。シェルはそれを保存せず、履歴展開はあなた自身が実行したコマンド、またはそれらのコマンドの一部専用です。

これにより、最後のコマンドを再実行し、stdoutstderr|&)の両方をコマンド置換にパイプするというアプローチが残ります。heemaylの答えはこれを達成しますが、シェルはエイリアスを展開する前ではなく、展開する前に履歴を展開するため、エイリアスで使用することはできません。

を使用して関数で有効にしても、シェル関数でも履歴展開を機能させることはできませんset -H!!関数内で拡張されることはないだろうと私は疑い、それがどのように拡張されるかはわかりませんが、今はなぜそうでないのか正確にはわかりません。

したがって、非常に少ない入力でこれを実行できるように設定する場合は、履歴展開の代わりにfcシェル組み込みコマンドを使用して、履歴から最後のコマンドを抽出する必要があります。これには、履歴の展開が無効になっている場合でも機能するという追加の利点があります。

スーパーユーザーのbash履歴展開を含むエイリアスの作成に対するGordon Davisson回答に示されているように、をシミュレートします。これをheemaylのコマンド接続すると、次の結果が得られます。$(fc -ln -1)!!!! $(!! |& tail -1)

$($(fc -ln -1) |& tail -1)

これは次のように機能します$(!! |& tail -1)が、エイリアス定義に入れることができます。

alias @@='$($(fc -ln -1) |& tail -1)'

その定義を実行するか、新しいシェルを開始.bash_aliasesまたは.bashrc開始してから、@@(またはエイリアスに名前を付けた)を入力するだけで、最後のコマンドからの出力の最終行を実行できます。

ek@Io:~$ alias @@='$($(fc -ln -1) |& tail -1)'
ek@Io:~$ evolution
The program 'evolution' is currently not installed. You can install it by typing:
sudo apt-get install evolution
ek@Io:~$ @@
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  evolution-common evolution-data-server evolution-data-server-online-accounts
....

これを別のスクリプトに移動するオプションはありますか?fcが何も出力しないため、最終的に失敗しました... fcは現在のシェルセッションの履歴を追跡するため、別のスクリプトに移動すると、空の履歴を持つ別のセッションが開きます。もちろん、...スクリプトのfc行の上にコマンドがあり、そのうちの1つが実行されました。だから、スクリプトに「コンテキスト」がそれを実行したbashセッションであることを伝えるオプションがあるのだろうか。同様に、#/ binに/ bashや何かのためにいくつかの引数...!
Exterminator13

@ Exterminator13あなたが実行する別のスクリプトを意味する場合- または、組み込みで./scriptソースを作成するのではなく- わかりません。bashがシェルの終了時にファイルに追加したり、実行したときとか(参照します)。その場合、複数の対話型シェルのコマンドはインターリーブされます(実行した順序で表示されます)。すぐに追加することができます。。しかし、スクリプトで作業を行うには、まだやるべきことがあると思います。これに関する新しい質問を投稿することをお勧めします。そうした場合は、ここにリンクを張ってコメントしてください。.sourcehistory-a-whelp historyfc
エリアケイガン

2

Xでターミナルエミュレータを使用している場合gnome-terminalkonsoleまたはのように、xtermコピーと貼り付けなどのX選択を使用できます。

一次選択を使用する

トリプル左クリックで実行する行全体を選択します

次に、中央のボタンをクリックしてコマンドラインに貼り付けます

これは、ほとんどの端末エミュレーターで動作するはずです。クリップボードに触れることなく、プライマリ選択を利用します-それらは別々です。
技術的には、選択してから、1つの操作でコピーと貼り付けを組み合わせます。


1
@Timそれは本当です-私はそれを明確にします。
フォルカーシーゲル

1

Eliah Kaganが述べたように、シェルはコマンド出力を保存しません。ただし、screenを実行すると、コマンドを再実行せずに、最後のコマンドの最後の出力行を取得する方法があります

次の行を入力します~/.screenrc

register t "^a[k0y$ ^a]^a^L"
bind t process t

次に、Ctrl+ atを押して、現在のプロンプトに前の行を挿入できます。これを自動的にEnterキーを押すこともできますが、実行する前にチェックしてEnterキーを手動で押すことをお勧めします。

使い方:

  • register tという名前のバッファに文字列を登録しますt。それに続く文字列はキーシーケンスです。
  • bind tキーにバインドしますt(つまり、Ctrl+ を使用して実行しますat)。process tという名前のバッファのコンテンツを評価しますt
  • シーケンス自体の意味:
    • ^a[(= Ctrla[):コピーモードに入る
    • k1行上に0移動し、行の先頭に移動し、行のy$末尾までコピーし、(スペース)コマンドを完了します
    • ^a](= Ctrla]):コピーしたコンテンツを貼り付ける
    • ^a^L(= CtrlaCtrlL)画面を更新します(そうしないと、自分で入力しなかったため、貼り付けたコンテンツが表示されません
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.