Bashを使用して最後のコマンドの出力を変数に自動的にキャプチャしますか?


139

最後に実行したコマンドの結果を後続のコマンドで使用できるようにしたい。例えば、

$ find . -name foo.txt
./home/user/some/directory/foo.txt

ここで、エディタでファイルを開いたり、ファイルを削除したり、他のことを実行したりしたいとします。たとえば、

mv <some-variable-that-contains-the-result> /some/new/location

どうすればできますか?多分いくつかのbash変数を使用していますか?

更新:

明確にするために、私は手動で物事を割り当てたくありません。私が求めているのは、組み込みのbash変数のようなものです。

ls /tmp
cd $_

$_前のコマンドの最後の引数を保持します。同様のものが欲しいのですが、最後のコマンドの出力が必要です。

最終更新:

セスの答えはかなりうまくいきました。心に留めておくべきこと:

  • touch /tmp/x初めてソリューションを試すときに忘れないでください
  • 結果は、最後のコマンドの終了コードが成功した場合にのみ保存されます

あなたの編集を見た後、私は私の答えを削除することを考えました。あなたが探しているビルトインがあるかしら。
taskinoor 2011年

ビルトインは見つかりませんでした。私はそれを実装することが可能かどうか疑問に思っていました.. .bahsrcを通じて?かなり便利な機能だと思います。
アルマンディーノ

私ができることは、出力をファイルにリダイレクトするか、パイプするか、キャプチャすることだけです。そうしないと、保存されません。
バンディ

3
シェルとターミナルの協力なしにそれを行うことはできません、そしてそれらは一般に協力しません。コマンドラインからの最後の出力を再利用するにはどうすればよいですか?も参照してくださいそして前のコマンド出力からテキストを使用した上でUnixのスタック交換
Gilles「SO-悪をやめる」

6
コマンドの出力がキャプチャされない主な理由の1つは、出力が任意の大きさ(一度に数メガバイト)になる可能性があるためです。確かに、必ずしもそれほど大きくはありませんが、大きな出力は問題を引き起こします。
ジョナサンレフラー、

回答:


71

これは本当にハックなソリューションですが、ほとんどの場合うまくいくようです。テスト中^Cに、コマンドラインでa を取得するときにうまく動作しないことがあるのに気付きましたが、少し良くするために少し調整しました。

このハックは対話モードのみのハックであり、私は誰にも推奨しないと確信しています。バックグラウンドコマンドは、通常よりも定義されていない動作を引き起こす可能性があります。他の答えは、プログラムで結果を得る良い方法です。


そうは言っても、ここに「解決策」があります:

PROMPT_COMMAND='LAST="`cat /tmp/x`"; exec >/dev/tty; exec > >(tee /tmp/x)'

このbash環境変数を設定し、必要に応じてコマンドを発行します。 $LAST通常、あなたが探している出力があります:

startide seth> fortune
Courtship to marriage, as a very witty prologue to a very dull play.
                -- William Congreve
startide seth> echo "$LAST"
Courtship to marriage, as a very witty prologue to a very dull play.
                -- William Congreve

1
@armandino:もちろんこれにより、標準出力で端末と対話することを期待しているプログラムが期待どおりに機能しなかったり(多かれ少なかれ)、$ LASTに奇妙なものが格納されます(emacs)。しかし、私はそれがあなたが得るつもりだと同じくらい良いと思います。他の唯一のオプションは、(type)scriptを使用してEVERYTHINGのコピーをファイルに保存し、PROMPT_COMMANDを使用して最後のPROMPT_COMMAND以降の変更を確認することです。ただし、これには不要なものが含まれます。でも、これにしたいものにもっと近いものを見つけるつもりはないと思います。
セスロバートソン

2
もう少し進むと、とのPROMPT_COMMAND='last="$(cat /tmp/last)";lasterr="$(cat /tmp/lasterr)"; exec >/dev/tty; exec > >(tee /tmp/last); exec 2>/dev/tty; exec 2> >(tee /tmp/lasterr)'両方が提供されます。$last$lasterr
ℝaphink

@cdosborn:デフォルトでは、manはページャーを介して出力を送信します。以前のコメントで述べたように、「標準出力の端末と対話することを期待しているプログラムは期待どおりに機能しない(多かれ少なかれ)」。多かれ少なかれポケットベルです。
セスロバートソン

取得:No such file or directory
Francisco Corrales Morales

@Raphink訂正を指摘したかっただけ2> >(tee /tmp/lasterr 1>&2)です。標準出力teeを標準エラーにリダイレクトする必要があるため、提案は終了するはずです。
Hugues 2017年

90

これを自動的に行う変数については知りません。結果をコピーして貼り付けるだけでなく、何かを実行するには、実行したことをすべて再実行できます。たとえば、

vim $(!!)

!!「前のコマンド」を意味する履歴拡張はどこにありますか。

スペースやその他の文字を含む単一のファイル名があり、適切な引数の解析を妨げる可能性がある場合は、結果を引用してください(vim "$(!!)")。引用符を付けないでおくと、スペースや他のシェル解析トークンを含まない限り、複数のファイルを一度に開くことができます。


1
コピー貼り付けは、私が通常このような場合に行うことです。これは、通常、コマンドの出力の一部しか必要としないためです。あなたがそれを最初に言及したのは驚きです。
Bruno De Fraine

4
あなたが持つ少数のキーストロークとほぼ同じ行うことができますvim `!!`
psmears

承認された回答との違いを確認するには、実行date "+%N"してからecho $(!!)
Marinos An

20

バッシュは醜い言語の一種です。はい、出力を変数に割り当てることができます

MY_VAR="$(find -name foo.txt)"
echo "$MY_VAR"

ただしfind、Bash変数に割り当てられたときに通知なしで変更されるため、結果が1つだけ返され、その結果にキャリッジリターンやラインフィードのような「奇数」文字が含まれていないことを最も期待してください。

ただし、変数を使用するときは、変数を正しく引用するように注意してください。

たとえば、find'を使用してファイルを直接-execdir操作することをお勧めします(マニュアルを参照)。

find -name foo.txt -execdir vim '{}' ';'

または

find -name foo.txt -execdir rename 's/\.txt$/.xml/' '{}' ';'

5
割り当てるときに暗黙的に変更されるのではなくエコーするときに変更されますあなたecho "${MY_VAR}"はこれが事実であることを確認するためにやらなければならないだけです。
paxdiablo

13

これを行う方法は複数あります。1つの方法はv=$(command)、コマンドの出力をに割り当てるを使用することvです。例えば:

v=$(date)
echo $v

また、バッククォートも使用できます。

v=`date`
echo $v

バッシュ初心者ガイドから、

古いスタイルのバッククォート形式の置換が使用される場合、バックスラッシュは、「$」、「 `」、または「\」が後に続く場合を除いて、文字どおりの意味を保持します。バックスラッシュが前に付いていない最初のバックティックは、コマンド置換を終了します。"$(COMMAND)"形式を使用する場合、括弧内のすべての文字がコマンドを構成します。特別に扱われるものはありません。

編集:問題の編集後、これはOPが探しているものではないようです。私の知る限り$_、最後のコマンドの出力のような特別な変数はありません。


11

とても簡単です。逆引用符を使用します。

var=`find . -name foo.txt`

そして、あなたは将来いつでもそれを使うことができます

echo $var
mv $var /somewhere

11

ディスクラマー:

  • この答えは半年遅れです:D
  • 私はtmuxのヘビーユーザーです
  • これを機能させるには、tmuxでシェルを実行する必要があります

tmuxで対話型シェルを実行すると、現在端末に表示されているデータに簡単にアクセスできます。いくつかの興味深いコマンドを見てみましょう:

  • tmux capture-pane:これは、表示されたデータをtmuxの内部バッファーの1つにコピーします。現在表示されていない履歴をコピーできますが、今は興味がありません
  • tmux list-buffers:これは、キャプチャされたバッファに関する情報を表示します。最新のものは番号0になります。
  • tmux show-buffer -b(buffer num):これは、指定されたバッファーの内容を端末に出力します
  • tmux paste-buffer -b(buffer num):これは、指定されたバッファーの内容を入力として貼り付けます

ええ、これは今私たちに多くの可能性を与えます:)私に関しては、私は単純なエイリアスを設定しました:alias L="tmux capture-pane; tmux showb -b 0 | tail -n 3 | head -n 1"そして今、私は$(L)それを取得するために単に使用する最後の行にアクセスする必要があるたびに。

これは、プログラムが使用する出力ストリーム(stdinまたはstderr)、印刷方法(ncursesなど)、およびプログラムの終了コード(データを表示する必要があるだけ)とは無関係です。


ちょっと、このtmuxのヒントをありがとう。私は基本的にOPと同じものを探していて、あなたのコメントを見つけ、あなたが言及したtmuxコマンドとVimモーションを使用して前のコマンドの出力の一部を選択して貼り付けることができるようにシェルスクリプトをコード化しました:github.com/ bgribble / lw
Bill Gribble

9

私はあなたがあなたのシェルを含むスクリプトにあなたのシェルを設定することを含む解決策をハックすることができるかもしれないと思う:

#!/bin/sh
bash | tee /var/log/bash.out.log

次に$PROMPT_COMMAND、区切り文字を出力するように設定した場合_、そのログの最後のチャンクを取得するヘルパー関数(と呼ばれる場合があります)を記述できるため、次のように使用できます。

% find lots*of*files
...
% echo "$(_)"
... # same output, but doesn't run the command again

7

bashプロファイルに次のエイリアスを設定できます。

alias s='it=$($(history | tail -2 | head -1 | cut -d" " -f4-))'

次に、任意のコマンドの後に「s」と入力すると、結果をシェル変数「it」に保存できます。

したがって、使用例は次のようになります。

$ which python
/usr/bin/python
$ s
$ file $it
/usr/bin/python: symbolic link to `python2.6'

ありがとうございました!これはgrab、最後のコマンドからn行目をクリップボードにコピーする関数を実装するために必要なものですgist.github.com/davidhq/f37ac87bc77f27c5027e
davidhq

6

私はbashここの提案からこの関数を抽出しました:

grab() {     
  grab=$("$@")
  echo $grab
}

次に、あなたはただ行います:

> grab date
Do 16. Feb 13:05:04 CET 2012
> echo $grab
Do 16. Feb 13:05:04 CET 2012

アップデート:匿名ユーザーが交換するために提案さechoによってprintf '%s\n'それはのようなオプション処理しないという利点がある-eつかんテキスト内を。したがって、そのような特殊性を期待または経験する場合は、この提案を検討してください。別のオプションは、cat <<<$grab代わりに使用することです。


1
わかりました、厳密に言えば、これは答えではありません。「自動」の部分が完全になくなっているためです。
Tilman Vogel

cat <<<$grabオプションを説明できますか?
leoj

1
引用元man bash:「ヒア文字列:ヒアドキュメントのバリアント。形式は次のとおりです<<<word。単語は展開され、標準入力でコマンドに提供されます。」したがって、の値は標準$grab入力catに渡され、cat標準出力にフィードバックされるだけです。
Tilman Vogel

5

「最後に実行されたコマンドの結果を後続のコマンドで使用できるようにしたい」と言うことで、私は仮定します-単に検索するだけでなく、任意のコマンドの結果を意味します。

その場合-xargsはあなたが探しているものです。

find . -name foo.txt -print0 | xargs -0 -I{} mv {} /some/new/location/{}

または、最初に出力を確認したい場合:

find . -name foo.txt -print0

!! | xargs -0 -I{} mv {} /some/new/location/{}

このコマンドは、複数のファイルを扱い、パスやファイル名にスペースが含まれている場合でも、チャームのように機能します。

コマンドのmv {} / some / new / location / {}の部分に注目してください。このコマンドは、前のコマンドで出力された各行に対して作成および実行されます。ここでは、前のコマンドで出力された行が{}の代わりに置き換えられています。

xargsのmanページからの抜粋:

xargs-標準入力からコマンドラインを構築して実行する

詳細については、manページを参照してください。 man xargs


5

バックティックで出力をキャプチャします。

output=`program arguments`
echo $output
emacs $output

4

私は通常、ここにいる他の人が提案したことをしています...割り当てなしで:

$find . -iname '*.cpp' -print
./foo.cpp
./bar.cpp
$vi `!!`
2 files to edit

あなたが好きなら、あなたはより洗練されたものになることができます:

$grep -R "some variable" * | grep -v tags
./foo/bar/xxx
./bar/foo/yyy
$vi `!!`

2

最後のコマンドを再実行して出力を取得するだけの場合は、単純なbash変数が機能します。

LAST=`!!`

したがって、次のコマンドで出力に対してコマンドを実行できます。

yourCommand $LAST

これにより、新しいプロセスが生成されてコマンドが再実行され、出力が表示されます。コマンド出力用のbash履歴ファイルが本当に必要なようです。つまり、bashが端末に送信する出力をキャプチャする必要があります。/ devまたは/ procを監視するために何かを書くことができますが、それは面倒です。また、途中でteeコマンドを使用して用語とbashの間に「特別なパイプ」を作成し、出力ファイルにリダイレクトすることもできます。

しかし、どちらもハックソリューションの一種です。最良のことは、出力ロギングを備えたより近代的な端末であるターミネーターだと思います。最後のコマンドの結果については、ログファイルを確認してください。上記と同様のbash変数は、これをさらに簡単にします。


1

コマンドを実行し、結果を変数に格納することを決定した後、これを行う1つの方法を次に示します。

$ find . -name foo.txt
./home/user/some/directory/foo.txt
$ OUTPUT=`!!`
$ echo $OUTPUT
./home/user/some/directory/foo.txt
$ mv $OUTPUT somewhere/else/

または、事前に変数の結果が必要であることがわかっている場合は、バックティックを使用できます。

$ OUTPUT=`find . -name foo.txt`
$ echo $OUTPUT
./home/user/some/directory/foo.txt

1

既存の回答の代わりとして:whileファイル名に次のような空白を含めることができる場合に使用します。

find . -name foo.txt | while IFS= read -r var; do
  echo "$var"
done

私が書いたように、ファイル名に空白が必要な場合にのみ、違いが関係します。

注意:組み込みのものは、出力に関するものではなく、最後のコマンドのステータスに関するものです。


1

使用できます!!:1。例:

~]$ ls *.~
class1.cpp~ class1.h~ main.cpp~ CMakeList.txt~ 

~]$ rm !!:1
rm class1.cpp~ class1.h~ main.cpp~ CMakeList.txt~ 


~]$ ls file_to_remove1 file_to_remove2
file_to_remove1 file_to_remove2

~]$ rm !!:1
rm file_to_remove1

~]$ rm !!:2
rm file_to_remove2

この表記は、コマンドから番号付き引数を取得します。ここで「$ {parameter:offset}」のドキュメントを参照してください:gnu.org/software/bash/manual/html_node/…。より多くの例については、こちらを参照:howtogeek.com/howto/44997/...
アレクサンダー・バード

1

同様のニーズがあり、最後のコマンドの出力を次のコマンドに使用したいと思いました。のように| (パイプ)。例えば

$ which gradle 
/usr/bin/gradle
$ ls -alrt /usr/bin/gradle

のようなものに-

$ which gradle |: ls -altr {}

解決策:このカスタムパイプを作成しました。xargsを使用して本当に簡単-

$ alias :='xargs -I{}'

基本的に、xargsの短縮形を作成しても何も起こらず、魅力のように機能し、本当に便利です。.bash_profileファイルにエイリアスを追加するだけです。


1

これは、ファイル記述子の魔法とlastpipeシェルオプションを使用して実行できます。

これはスクリプトで行う必要があります-「ラストパイプ」オプションはインタラクティブモードでは機能しません。

これが私がテストしたスクリプトです:

$ cat shorttest.sh 
#!/bin/bash
shopt -s lastpipe

exit_tests() {
    EXITMSG="$(cat /proc/self/fd/0)"
}

ls /bloop 2>&1 | exit_tests

echo "My output is \"$EXITMSG\""


$ bash shorttest.sh 
My output is "ls: cannot access '/bloop': No such file or directory"

ここで私がしていることは:

  1. シェルオプションを設定しますshopt -s lastpipe。ファイル記述子が失われるため、これがないと機能しません。

  2. 私のstderrも確実にキャプチャされるようにする 2>&1

  3. stdinファイル記述子を参照できるように、出力を関数にパイプします。

  4. /proc/self/fd/0stdinであるファイル記述子の内容を取得して変数を設定します 。

スクリプトのエラーをキャプチャするためにこれを使用しているため、コマンドに問題がある場合は、スクリプトの処理を停止してすぐに終了できます。

shopt -s lastpipe

exit_tests() {
    MYSTUFF="$(cat /proc/self/fd/0)"
    BADLINE=$BASH_LINENO
}

error_msg () {
    echo -e "$0: line $BADLINE\n\t $MYSTUFF"
    exit 1
}

ls /bloop 2>&1 | exit_tests ; [[ "${PIPESTATUS[0]}" == "0" ]] || error_msg

この方法で2>&1 | exit_tests ; [[ "${PIPESTATUS[0]}" == "0" ]] || error_msg、チェックしたいすべてのコマンドの後ろに追加できます。

これで出力を楽しむことができます!


0

これは厳密にはbashソリューションではありませんが、sedでパイピングを使用して、前のコマンド出力の最後の行を取得できます。

まず、フォルダ「a」にあるものを確認しましょう

rasjani@helruo-dhcp022206::~$ find a
a
a/foo
a/bar
a/bat
a/baz
rasjani@helruo-dhcp022206::~$ 

次に、lsとcdを使用した例は、sed&pipeingに変わり、次のようなものになります。

rasjani@helruo-dhcp022206::~$ cd `find a |sed '$!d'`
rasjani@helruo-dhcp022206::~/a/baz$ pwd
/home/rasjani/a/baz
rasjani@helruo-dhcp022206::~/a/baz$

したがって、実際の魔法はsedで発生し、コマンドの出力をパイプでsedに送り、sedは最後の行を出力します。これは、バックティックのパラメーターとして使用できます。または、それをxargsに組み合わせることもできます。(シェルの「man xargs」はあなたの友達です)


0

シェルには、最後のコマンドのエコー結果を格納するperlのような特別な記号はありません。

awkでパイプ記号を使用する方法を学びます。

find . | awk '{ print "FILE:" $0 }'

上記の例では、次のことができます。

find . -name "foo.txt" | awk '{ print "mv "$0" ~/bar/" | "sh" }'

0
find . -name foo.txt 1> tmpfile && mv `cat tmpfile` /path/to/some/dir/

汚いが、さらに別の方法です。


見つける。-name foo.txt 1> tmpfile && mv `cat tmpfile` path / to / some / dir && rm tmpfile
Kevin

0

コマンドの出力を特定のファイルにパイプするのが少し面倒になるのを覚えています。私の解決策は.bash_profile、ファイルの出力をキャッチし、必要なときに結果を返す関数です。

これの利点は、コマンド全体を再実行する必要がないことです(find重要な可能性がある他の長期実行コマンドを使用する場合)

非常に簡単です、これをあなたに貼り付けてください.bash_profile

脚本

# catch stdin, pipe it to stdout and save to a file
catch () { cat - | tee /tmp/catch.out}
# print whatever output was saved to a file
res () { cat /tmp/catch.out }

使用法

$ find . -name 'filename' | catch
/path/to/filename

$ res
/path/to/filename

この時点では| catch、すべてのコマンドの最後に追加する傾向があります。これを行うためのコストはかからず、終了までに長い時間がかかるコマンドを再実行する必要がなくなるためです。

また、テキストエディターでファイル出力を開きたい場合は、次のようにします。

# vim or whatever your favorite text editor is
$ vim <(res)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.