現在のシェル内でコマンドの出力を実行するにはどうすればよいですか?


92

私は、ファイルからコンテンツを取得して現在のシェル内で実行するsource(aka .)ユーティリティをよく知っています。

今、私はいくつかのテキストをシェルコマンドに変換し、次のようにそれらを実行しています:

$ ls | sed ... | sh

ls単なるランダムな例であり、元のテキストは何でもかまいません。sedまた、テキストを変換するための単なる例です。興味深いビットはshです。到達したものは何でもパイプshして実行します。

私の問題は、それは新しいサブシェルを開始することを意味します。現在のシェル内でコマンドを実行したいのですが。私がsource some-fileテキストファイルにコマンドを持っていれば、私ができるように。

汚れているので一時ファイルを作成したくありません。

または、現在のシェルとまったく同じ特性でサブシェルを開始したいと思います。

更新

わかりました。バッククォートを使用したソリューションは確かに機能しますが、出力を確認および変更しているときにこれを行う必要があることが多いので、最終的に結果を何かにパイプする方法があればもっといいと思います。

悲しいアップデート

ああ、/dev/stdin物事はとてもきれいに見えましたが、もっと複雑なケースでは、うまくいきませんでした。

だから、私はこれを持っています:

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/git mv -f $1 $2.doc/i' | source /dev/stdin

これにより、すべての.docファイルの拡張子が小文字になります。

ちなみに、これはで処理できますがxargs、それだけではありません。

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/$1 $2.doc/i' | xargs -L1 git mv

したがって、前者を実行すると、すぐに終了し、何も起こりません。


最初に一時ファイルにパイプしてからソースを作成するときに、複雑なコマンドは機能しますか?そうでない場合、生成された出力の問題は何ですか?ファイル名にスペースが含まれている場合、または特定のシーケンスが適切にエスケープされていない場合、コマンドの出力は機能しません。少なくとも$ 1と$ 2.doc前後の見積もりを追加したいと思います。
Kaleb Pederson

これを元のシェルで実行しなければならない正当な理由はありますか?-これらの例は現在のシェルを操作しないため、操作しても何も得られません。簡単な解決策は、出力をファイルにリダイレクトし、そのファイルをソースすることです
いいえ

@kaleb出力は正常に実行されます。この特定のケースでは、たとえ私がshにパイプしたとしても。ファイル名はスペースセーフですが、注意していただきありがとうございます。元のシェルの@nosgit環境変数。繰り返しますが、これらは単なる例です。問題は人生です。
kch 2009

割り当てられた変数を維持する必要がある場合、ソース/ dev / stdinは機能しませんでした。freenode bashのgeirhaは、mywiki.wooledge.org / BashFAQ / 024を指して、私のために機能するプロセス置換ソース<(コマンド)を試すことを提案しました
srcerer 2015年

回答:


88
$ ls | sed ... | source /dev/stdin

更新:これは、bash 4.0、tcsh、およびdash(に変更sourceした場合.)で機能します。どうやらこれはbash3.2ではバグがありました。以下からのbash 4.0のリリースノート

`。 'を引き起こすバグを修正しました。デバイスや名前付きパイプなどの非正規ファイルからのコマンドの読み取りと実行に失敗する。


ああ、そしてあなたがシェルをリストしているので、それはzshでも機能します。
kch 2009

2
msys / mingw(/ devフォルダー(またはデバイスさえも)がない)で試すまで、これは独創的だと思いました!eval、$()、およびbackticksの多くの組み合わせを試しました ``。何も機能しませんでした、それで私はついに出力を一時ファイルにリダイレクトし、一時ファイルをソースし、それを削除しました。基本的に「sed ...>/tmp/$$.tmp&&。/tmp/$$.tmp&&rm / tmp / $$ .tmp "。誰もが一時ファイルなしでmsys / mingwソリューションを手に入れました???
chriv 2012

10
ただの注意:これは、期待どおりにエクスポートステートメントを処理していないようです。パイプされた文字列にexportステートメントがある場合、その後、exportet変数はターミナル環境で使用できなくなりますが、evalエクスポートでは完全に機能します。
Phil

lastpipeオプションを設定せずにbashで、これは同じようにサブシェルを作成する| bashだろう
そのほかの男

1
MacOSXには通常bash3.2があり(おそらくYosemiteには4.0がありますか?)、私のユースケースではラストパイプのトリックが必要です(各行をsedで前処理して 'export'を追加することにより、ファイル内のすべての変数をエクスポートします)。eval "$(sed ...)"アプローチを進めるために-しかし、3.2バグヘッズアップに感謝します!
Alex Dupuy

135

このevalコマンドはまさにこの目的のために存在します。

eval "$( ls | sed... )"

bashマニュアルの詳細:

eval

          eval [arguments]

引数は1つのコマンドに連結され、次に読み取られて実行され、その終了ステータスがevalの終了ステータスとして返されます。引数がないか、空の引数しかない場合、戻りステータスはゼロです。


8
ここでの唯一の問題は、コマンドを区切るために;を挿入する必要があるかもしれないということです。私はこの方法を自分で使用して、AIX、Sun、HP、およびLinuxで作業しています。
タンクタルス2009

Tanktalus、そのコメントに感謝します、それはちょうど私のスクリプトを機能させました。私のマシンでは、evalは改行でコマンドを分離せず、ソースの使用はセミコロンでも機能しません。セミコロンを使用した評価が解決策です。私はあなたにいくつかのポイントを与えることができればいいのにと思います。
ミラノバブシュコフ2011

2
@MilanBabuškov:私はあなたのために彼をランク付けしました;-)
Phil

3
これは素晴らしいです。ただし、私のユースケースでは、次のように$()を引用符で囲む必要がありましたeval "$( ssh remote-host 'cat ~/.bash_profile' )"。実際にbash3.2を使用していることに注意してください。
Zachary Murray

3
これは、受け入れられた答えがうまくいかなかった私にとってはうまくいきます。
ダンテネンバウム2014年

37

うわー、これは古い質問だと知っていますが、最近、まったく同じ問題を抱えていることに気づきました(それが私がここにたどり着いた方法です)。

とにかく-私はsource /dev/stdin答えが好きではありませんが、私はより良いものを見つけたと思います。それは実際には一見単純です:

echo ls -la | xargs xargs

いいですね 実際には、これはまだあなたが望むことをしません。なぜなら、複数の行がある場合、各コマンドを別々に実行するのではなく、それらを単一のコマンドに連結するからです。だから私が見つけた解決策は:

ls | ... | xargs -L 1 xargs

この-L 1オプションは、コマンドの実行ごとに(最大で)1行を使用することを意味します。注:行が末尾のスペースで終わっている場合は、次の行と連結されます。したがって、各行がスペース以外で終わっていることを確認してください。

最後に、あなたはすることができます

ls | ... | xargs -L 1 xargs -t

実行されるコマンドを確認します(-tは詳細です)。

誰かがこれを読んでくれることを願っています!


31

プロセス置換を使用してみてください。これにより、コマンドの出力が一時ファイルに置き換えられ、一時ファイルを入手できます。

source <(echo id)

7
これはどのような魔術ですか?
paulotorrens 2015

1
これも私の最初の考えでした。私はevalソリューションの方が好きですが。@PauloTorrens <(xyz)は、単にxyzを実行し、<(xyz)をxyzの出力を書き込むファイルの名前に置き換えます。それは理解することはとても簡単ですか、たとえば行うことによって、この作品:echo <(echo id)、出力を与える/dev/fd/12(12例)で、cat <(echo id)出力を与えid、その後、source <(echo id)単に書くのと同じ出力を与えるid
netigger

2
@PauloTorrens、これはプロセス置換と呼ばれます。公式の説明についてはリンクされたドキュメントを参照してください。ただし、簡単に言うと、「<()」はこのような場合を念頭に置いて設計された特別な構文です。
マークストスバーグ2016年

1
@MarkStosberg、これが特別な構文なのか、それとも単にサブシェルが(...)リダイレクトされただけなのか知っていますか?
paulotorrens 2016年

2
@PauloTorrens、これはプロセス置換のためだけの特別な構文です。
Mark Stosberg 2016年

6

私はこれが質問に対する「正しい答え」であると信じています:

ls | sed ... | while read line; do $line; done

つまり、whileループにパイプすることができます。readコマンドコマンドは、そのから1行を取りstdin、変数に代入します$line$line次に、ループ内で実行されるコマンドになります。そして、入力にそれ以上の行がなくなるまで続きます。

これは、一部の制御構造(別のループなど)では機能しませんが、この場合は問題ありません。


5
`ls | sed ...`

ちょっとls | sed ... | source -綺麗な気がしますが、残念ながら意味がsourceわかりません。-stdin


mark4oの答えを見た後、ずっと私たちの顔に正しかったように感じませんか?
kch 2009

ええ、ええ。そのようなものが存在することを私は決して覚えていません。
カオス

1

あなたの解決策は、バッククォートによるコマンド置換だと思います:http//tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

セクション3.4.5を参照してください


3
bashを使用している場合は、$( )構文が優先される場合があります。「コースのバッククォートでの作業よりシェル...
dmckee ---元司会者の子猫

6
$(コマンド)と$((1 + 1))は、すべてのposixシェルで機能します。多くの人がvimがそれらを構文エラーとしてマークすることによって延期されていると思いますが、それはvimがほとんど使用されていない元のBourneシェルを強調しているからです。vimを正しくハイライトするには、これを.vimrcに入れます。letg:is_posix = 1
pixelbeat 2009

1

bash 3.2(macos)でmark4oのソリューションを使用するには、次の例のようにパイプラインの代わりにhere文字列を使用できます。

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"

またはバッククォートを使用します。/ dev / stdin <<< `grep '^ alias'〜 / .profile`
William

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.