1つの引数で2つのコマンドを実行します(スクリプトなし)


28

入力を2回入力せずに、1つの入力で2つのコマンドを実行するにはどうすればよいですか?

たとえば、statコマンドはファイルについて多くのことを伝えますが、ファイルの種類を示しません:

stat fileName

fileコマンドは、ファイルのタイプを示します。

file fileName

これは次の方法で1行で実行できます。

stat fileName ; file fileName

ただし、fileNameを2回入力する必要があります。

同じ入力で両方のコマンドを(入力または入力の変数を2回入力せずに)実行するにはどうすればよいですか?

Linuxでは、出力をパイプする方法を知っていますが、入力をパイプするにはどうすればよいですか?


14
明確にするために、これらは「入力」ではなく、引数(別名パラメーター)です。 入力は、<(ファイルから左側への|入力)または(ストリームから右側への入力)経由でパイプできます。違いがあります。
goldilocks

6
あなたの質問への答えではなく、あなたの問題への答えかもしれません:bashでは、yank-last-argコマンド(デフォルトのショートカットMeta-.またはMeta-_;はMetaしばしば左Altキーです)は前のコマンドラインから最後のパラメーターを現在のものにコピーします。したがって、実行後、再度入力するstat fileName必要はfile [Meta-.]ありませんfileName。私はいつもそれを使います。
ドゥブ14年

2
その差は、ということである<>されているリダイレクトではなく、配管。パイプラインは2つのプロセスを接続しますが、リダイレクトは単にstdin / stdoutを再割り当てします。
BSD

@bdowning:ええ、でもあなたはパイプをリダイレクトしています。パイプラインの開始をリダイレクトしてディスク上のファイルから読み取るか、パイプラインの終了をリダイレクトしてディスク上のファイルに書き込むことができます。まだパイピング。;-)
ジェームズヘイ

回答:


22

別の方法を次に示します。

$ stat filename && file "$_"

例:

$ stat /usr/bin/yum && file "$_"
  File: ‘/usr/bin/yum’
  Size: 801         Blocks: 8          IO Block: 4096   regular file
Device: 804h/2052d  Inode: 1189124     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Context: system_u:object_r:rpm_exec_t:s0
Access: 2014-06-11 22:55:53.783586098 +0700
Modify: 2014-05-22 16:49:35.000000000 +0700
Change: 2014-06-11 19:15:30.047017844 +0700
 Birth: -
/usr/bin/yum: Python script, ASCII text executable

それはで動作bashしてzsh。それはまたで動作mkshし、dashしかし、ときにのみ、対話型。AT&T kshでは、がfile "$_"別の行にあるときにのみ機能しstatます。


1
!$1があるため動作しません!$(これ以前に入力したコマンドラインのではなく、statコマンドの)最後の履歴イベントの最後の単語に展開されます。
ステファンシャゼル14年

@StéphaneChazelas:ああ、はい、私の間違い、私はそれを削除しました。
cuonglm

32

おそらくこれのために私のナックルをラップするつもりです、ここにトリックを行うように見えるbashブレースの拡張とevalのハッキーな組み合わせがあります

eval {stat,file}" fileName;"


2
ブレースの拡張はでcshはなくで始まったbashbashからコピーしましたksh。このコードは、csh、tcsh、ksh、zsh、fish、bashのすべてで機能します。
ステファンシャゼル

1
引用が原因で、fileNameを「タブアウト」(オートコンプリート)することができなくなります。
ロニービズ14年

申し訳ありませんが、私は抵抗することができませんでしたどちらか
tomd

申し訳ありませんが、ここの問題は何evalですか?(トップコメントを参照)
user13107

28

ではzsh、匿名関数を使用できます。

(){stat $1; file $1} filename

esラムダ:

@{stat $1; file $1} filename

次のこともできます。

{ stat -; file -;} < filename

(アクセス時間を更新するため、stat最初に行うfile)。

私がやる:

f=filename; stat "$f"; file "$f"

しかし、それが変数の目的です。


3
{ stat -; file -;} < filename魔法です。statその標準入力がファイルに接続されていることを確認し、ファイルの属性を報告する必要がありますか?おそらく、従って標準入力可能にポインタを進めないfileスニフSTDIN内容に
iruvar

1
@ 1_CR、はい。stat -伝えstat行うにはfstat()、そのFD 0に
ステファンChazelas

4
{ $COMMAND_A -; $COMMAND_B; } < filename$ COMMAND_Aが実際に任意の入力を読み込む場合の変形は一般的なケースでは動作しません。例えば{ cat -; cat -; } < filename、ファイル名の内容を一度だけ印刷します。
マックスナナシー14年

2
stat -coreutils-8.0(2009年10月)以降、特別に処理されます。それ以前のバージョンでは、エラーが発生します(-以前の実験から呼び出されたファイルがない限り、誤った結果が得られます...)
mr .spuratic 14年

1
@ mr.spuratic。はい。BSD、IRIX、およびzshの統計でも認識されません。zshおよびIRIX statを使用すると、stat -f0代わりに使用できます。
ステファンシャゼル14年

9

これらの答えはすべて、多かれ少なかれ、私にとってはスクリプトのように思えます。本当にスクリプティングを必要とせず、キーボードに座ってシェルに入力していることを前提とするソリューションの場合は、次を試すことができます。

stat somefile Enter
file Esc_   Enter

少なくともbash、zsh、およびkshでは、viモードとemacsモードの両方で、Escapeキーを押してからアンダースコアを押すと、前の行の最後の単語が現在の行の編集バッファーに挿入されます。

または、履歴置換を使用できます。

stat somefile Enter
file!$Enter

bash、zsh、csh、tcsh、および他のほとんどのシェルで!$は、現在のコマンドラインが解析されると、前のコマンドラインの最後の単語に置き換えられます。


zsh / bashには他にどのようなオプションがありますEsc _か?公式ドキュメントとリンクしてください。
Abhijeet Rastogi


1
zshの:linux.die.net/man/1/zshzleと「挿入-ラストワード」の検索
godlygeek

1
@shadyabhi ^ bashとzshの標準キーバインディングの両方へのリンク。bashはreadlineを使用しているだけなので、bashのほとんどはreadlineライブラリを使用するアプリケーションで動作します。
godlygeek 14年

3

私がこれを合理的に行うことがわかったのは、xargsを使用することです。ファイル/パイプを取り、その内容をプログラム引数に変換します。これは、ストリームを分割して2つ以上のプログラムに送信するteeと組み合わせることができます。

echo filename | tee >(xargs stat) >(xargs file) | cat

他の多くの答えとは異なり、これはbashおよびLinuxの他のほとんどのシェルで機能します。私はこれが変数の良いユースケースであることを提案しますが、絶対に使用できない場合は、これはパイプと単純なユーティリティでのみそれを行うための最良の方法です(特に派手なシェルがないと仮定)。

さらに、ティーの後にこれら2つと同じ方法で追加するだけで、任意の数のプログラムに対してこれを行うことができます。

編集

コメントで示唆されているように、この回答にはいくつかの欠陥があります。最初の問題は、出力インターレースの可能性です。これは次のように修正できます。

echo filename | tee >(xargs stat) >(( wait $!; xargs file )) | cat

これにより、コマンドが順番に実行され、出力がインターレースされることはありません。

2番目の問題は、一部のシェルでは使用できないプロセス置換を回避することです。これは、Tの代わりにTパイプを使用することで実現できます。

echo filename | tpipe 'xargs stat' | ( wait $!; xargs file )

これは、ほぼすべてのシェルに移植可能であり(希望)、他のコマンドの問題を解決しますが、現在のシステムにはTパイプがないため、この最後のメモリから書き込みます。


1
プロセス置換はのみで動作kshの機能ですksh(パブリックドメイン変異体ではない)、bashzshstatfileが同時に実行されることに注意してください。したがって、それらの出力が絡み合っている可能性があります(| catその効果を制限するために出力バッファリングを強制するために使用していると思いますか?)。
ステファンシャゼル14年

@StéphaneChazelasあなたは猫について正しいです、それを使用するとき、私はそれを使用するときにインターレース入力のケースを生成することができませんでした。しかし、私は一時的に、よりポータブルなソリューションを作成するために何かをしようとしています。
バリティ14年

3

この答えは、他のいくつかに似ています。

stat filename   && file!#:1

前のコマンドの最後の引数を自動的に繰り返す他の回答とは異なり、この回答では次のことをカウントする必要があります。

ls -ld filename   && file!#:2

他のいくつかの回答とは異なり、これには引用符は必要ありません。

stat "useless cat"  &&  file !#:1

または

echo Sometimes a '$cigar' is just a !#:3.

2

これは、他の回答のカップルに似ていますが、よりポータブルで、この1 と比べてはるかに簡単なこの1

sh -c 'stat "$ 0"; ファイル "$ 0" ' ファイル名

または

sh -c 'stat "$ 1"; ファイル "$ 1" 'sh ファイル名

sh割り当てられてい$0ます。入力するのは3文字以上ですが、この(2番目の)形式は$0、インラインスクリプトに付ける名前であるため、推奨されます。これは、そのスクリプト用のシェルでエラーメッセージに、例えば、使用されています、ときのようなfileまたはstat見つからないか、何らかの理由で実行することはできません。


やりたいなら

stat filename 1  filename 2  filename 3 …; ファイルファイル1 ファイル名2 ファイル名3

任意の数のファイルについては、

sh -c 'stat "$ @"; ファイル "$ @" 'sh ファイル名1 ファイル名2 ファイル名3

"$@"はと同等であるため機能し"$1" "$2" "$3" …ます。


それ$0は、そのインラインスクリプトに付ける名前です。たとえば、そのスクリプトのシェルがエラーメッセージで使用します。いつ、fileまたはstat見つからない、何らかの理由で実行できないなど。したがって、ここで何か意味のあるものが必要です。触発されていない場合は、単に使用しますsh
ステファンシャゼル

1

少なくともあなたがやろうとしていることに関して、あなたは間違った質問をしていると思います。 statまた、fileコマンドはファイルの内容ではなくファイルの名前であるパラメーターを取ります。指定した名前で識別されるファイルの読み取りを行うコマンドの後半にあります。

パイプには2つの端があり、1つは入力として使用され、もう1つは出力として使用されます。これは、必要な結果が得られるまで、さまざまなツールでさまざまな処理を行う必要があるためです。出力をパイプする方法は知っているが、入力をパイプする方法はわからないと言うのは、原則として正しくありません。

あなたの場合、ファイル名の形式で入力を提供する必要があるため、パイプはまったく必要ありません。また、これらのツール(statおよびfile)が標準入力を読み取ったとしても、2番目のツールに到達したときにツールによって入力が変更されることはないため、パイプはここでは関係ありません。


1

これは、現在のディレクトリ内のすべてのファイル名で機能するはずです。

sh -c "$(printf 'stat -- "$1";file -- "$1";shift%.0b\n' *)" -- *

1
ファイル名に二重引用符、バックスラッシュ、ドル、バックティック、}...文字が含まれておらず、先頭がでないと仮定します-。一部のprintf実装(zsh、bash、ksh93)には%q。ではzsh、それは次のようになりますprintf 'stat %q; file %1$q\n' ./* | zsh
ステファンChazelas

@StephaneChezales-ハードクォートの可能性を除いて、すべてが修正されました。私はシングルでそれを扱うことができますsed s///が、それはスコープの範囲です-そして、人々がファイル名にそれを入れている場合は、ウィンドウを使用する必要があります。
mikeserv 14年

@StéphaneChazelas-nevermind-それらがいかに簡単に扱えるか忘れました。
mikeserv 14年

厳密に言えば、これは「入力を2回入力せずに同じ入力で両方のコマンドを実行するにはどうすればよいですか?」(強調を追加)という質問に対する答えではありません。あなたの答えは、現在のディレクトリ内のすべてのファイルに適用されます- そしてそれは、*二回。あなたも言うかもしれませんstat -- *; file -- *
スコット14

@Scott-入力は2回入力されません- *展開されます。の各引数のプラス2つのコマンドの展開が入力です。見る?* *printf commands\ %s\;shift *
mikeserv 14

1

ここには「入力」へのいくつかの異なる参照がありますので、最初にそれを理解することを念頭に置いていくつかのシナリオを示します。 最短形式の質問に対する迅速な回答

stat testfile < <($1)> outputfile

上記は、テストファイルの統計を実行し、STDOUTを取得(リダイレクト)し、それを次の特別な関数(<()部分)に含めて、それが何であれ最終結果を新しいファイル(outputfile)に出力します。ファイルが呼び出され、bashビルトインで参照されます(その後、毎回$ 1、新しい一連の指示を開始するまで)。

あなたの質問は素晴らしく、これを行うにはいくつかの答えと方法がありますが、実際にあなたが具体的にやっていることによって実際に変わります。

たとえば、それもループできます。これは非常に便利です。これの一般的な使用法は、擬似コードの考え方では、次のとおりです。

run program < <($output_from_program)> my_own.log

それを取り入れてその知識を拡張すると、次のようなものを作成できます。

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

これは、現在のディレクトリで単純なls -Aを実行し、次にls -Aからの各結果をループし、それらの結果のそれぞれでgrep "thatword"を実行し、前の結果のみを実行します。実際に「その単語」を含むファイルを見つけた場合は、printf(赤)。また、grepの結果を新しいテキストファイルfiles_that_matched_thatwordに記録します。

出力例:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

index.html

これらはすべてls -Aの結果を出力するだけで、特別なことは何もありません。今回はgrepに何かを追加します。

echo "thatword" >> newfile

次に、再実行します。

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

files_that_matched_thatword  index.html  newfile

Found a file: newfile:thatword

おそらくあなたが現在探しているよりも疲れる答えですが、このような便利なメモを保管しておくことは、将来の努力においてより多くの利益になると信じています。

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