web-dev-qa-db-ja.com

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

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

$ 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を忘れないでください
  • 結果は、最後のコマンドの終了コードが成功した場合にのみ保存されます
134
armandino

これは本当にハッキーな解決策ですが、ほとんどの場合はうまくいくようです。テスト中、私は時々、 ^C コマンドラインで、動作を少し改善するために少し調整しましたが。

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


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

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
70
Seth Robertson

私はこれを行う変数を知りません自動的に。結果をコピー&ペーストするだけでなく、何かを行うために、今やったことを何でもやり直すことができます。例えば

vim $(!!)

!!は、「前のコマンド」を意味する履歴展開です。

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

87
Daenyth

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

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

ただし、findが1つの結果のみを返し、キャリッジリターンやラインフィードなどの「奇妙な」文字が含まれていないことを強く願ってください。 。

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

ファイルを直接操作することをお勧めします。 find-execdirで(マニュアルを参照してください)。

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

または

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

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

v=$(date)
echo $v

また、逆引用符も使用できます。

v=`date`
echo $v

Bash初心者ガイド から

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

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

12
taskinoor

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

var=`find . -name foo.txt`

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

echo $var
mv $var /somewhere
10
Wes Hardaker

シェルを含むスクリプトにシェルを設定することを含むソリューションをハッキングできる可能性があると思います:

#!/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
9
Jay Adkisson

免責事項:

  • この答えは半年後です: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など)、およびプログラムの終了コードとは無関係です。データを表示するだけです。

8
Wiesław Herr

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'
7
Joe Tallett

ここでの提案からこの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

更新:匿名ユーザーは、echoprintf '%s\n'に置き換えることを提案しました。これは、取得したテキストの-eなどのオプションを処理しないという利点があります。したがって、このような特性を期待または経験している場合は、この提案を検討してください。別のオプションは、代わりにcat <<<$grabを使用することです。

6
Tilman Vogel

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

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

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

もしそうなら-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
ssapkota

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

$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 `!!`
4
halm

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

LAST=`!!`

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

yourCommand $LAST

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

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

2
Spencer Rathbun

コマンドを実行し、結果を変数に保存することを決定した後に行う方法の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
Nate W.

!!: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
1
rkm

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

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

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

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

1
0xC0000022L

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

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

のようなものに-

$ which gradle |: ls -altr {}

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

$ alias :='xargs -I{}'

基本的にxargsのショートハンドを作成しても何もありません。魅力のように機能し、本当に便利です。 .bash_profileファイルにエイリアスを追加するだけです。

1
eXc

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

スクリプトを使用して行う必要があります-「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/0ファイル記述子(stdin)の内容を取得して変数を設定します。

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

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を追加できます。

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

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

汚れているとはいえ、さらに別の方法です。

0
Kevin

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

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

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

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

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
rasjani

シェルには、最後のコマンドのエコー結果を保存するPerlのような特別なシンボルはありません。

Awkでパイプシンボルを使用する方法を学びます。

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

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

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