これはおそらく多くのFAQにあります-代わりに:
cat file | command
(これは猫の無駄な使用と呼ばれます)、正しい方法:
command < file
2番目の「正しい」方法では、OSは余分なプロセスを生成する必要がありません。
それを知っているにもかかわらず、私は2つの理由で役に立たない猫を使い続けました。
より美的-データが左から右にのみ均一に移動するのが好きです。 cat
を他のもの(gzcat
、echo
、...)に置き換えたり、2番目のファイルを追加したり、新しいフィルターを挿入したり(pv
、 mbuffer
、grep
...)。
場合によってはより高速になるかもしれないと「感じた」。 2つのプロセスがあるため、1番目(cat
)が読み取りを実行し、2番目が何でも実行するため、高速になります。そして、それらは並行して実行できるため、実行がより高速になる場合があります。
私のロジックは正しいですか(2番目の理由)?
ある新人が私の答えの1つとして [〜#〜] uuoc [〜#〜] をピン留めしようとした今日まで、私はこの賞を知らなかった。 cat file.txt | grep foo | cut ... | cut ...
でした。私は彼に私の心の一部を与えました、そして、そうすることをした後にだけ、彼は賞の起源とそうする習慣について言及してくれました。さらに検索すると、この質問につながりました。意識的に考慮しているにも関わらず、やや残念ながら、答えにはどれも私の理論的根拠が含まれていませんでした。
私は彼に応じるのを防御するつもりはなかった。結局、私の若い年には、コマンドをgrep foo file.txt | cut ... | cut ...
として書いていたでしょう。なぜなら、頻繁に単一のgrep
sを実行するたびに、ファイル引数の配置を学び、最初のものがパターンであり、後のものがファイル名であることを知っているからです。
私は質問に答えたときにcat
を使用することを意識的に選択しました。これは、「Linus Torvaldsの言葉による」という理由もありますが、主に機能の理由からです。
後者の理由はより重要なので、最初に説明します。ソリューションとしてパイプラインを提供するとき、再利用できると期待しています。パイプラインは、最後に追加されるか、別のパイプラインにスプライスされる可能性が非常に高いです。その場合、grepにファイル引数を指定すると再利用性が損なわれ、ファイル引数が存在する場合はエラーメッセージなしでsilentlyを使用する可能性が高くなります。 I. e。 grep foo xyz | grep bar xyz | wc
は、xyz
とbar
の両方を含む行数を期待しているときに、foo
にbar
を含む行数を示します。使用する前にパイプラインのコマンドの引数を変更する必要があると、エラーが発生しやすくなります。それに静かな失敗の可能性を加えてください、そして、それは特に陰湿な習慣になります。
前者の理由も重要ではありません。多くの「 good taste 」は、上記のサイレント障害のようなものに対する直感的な潜在意識の根拠であり、教育を必要とする人の中には、「しかし、その猫は役に立たないわけではありません」と言っています。
しかし、私が言った前者の「良い味」の理由を意識することも試みます。その理由は、Unixの直交設計の精神に関係しています。 grep
はcut
ではなく、ls
はgrep
ではありません。したがって、少なくともgrep foo file1 file2 file3
は設計の精神に反します。直交する方法はcat file1 file2 file3 | grep foo
です。現在、grep foo file1
はgrep foo file1 file2 file3
の特殊なケースにすぎず、同じように扱わなければ、少なくとも無駄な猫賞を避けるために脳のクロックサイクルを使い果たしていることになります。
それは、grep foo file1 file2 file3
が連結し、cat
が連結するため、cat file1 file2 file3
に適切であるという議論につながりますが、cat
はcat file1 | grep foo
で連結していないため、cat
と全能のUnixの両方の精神に違反しています。そうだとすると、Unixは1つのファイルの出力を読み取り、それを標準出力に吐き出すために別のコマンドが必要になります(ページネーションや、純粋な標準出力への吐き出しではありません)。したがって、cat file1 file2
と言うか、dog file1
と言って賞を獲得するのを避けるためにcat file1
を避けることを覚えておいてください。また、複数のファイルが指定されている場合はdog
の設計がエラーをスローするため、dog file1 file2
を避けることもできます。
願わくば、この時点で、ファイルをstdoutに吐き出す別のコマンドを含めず、また、他の名前を付けるのではなく、連結のためにcat
を命名することについて、Unixの設計者に同情してください。 <edit>
は<
の誤ったコメントを削除しました。実際、<
はファイルを標準出力に吐き出すための効率的なコピー禁止機能であり、 Unixデザイナーがこのために特別に何かを含めたパイプライン</edit>
次の質問は、それ以上の処理をせずに、単にファイルを吐くコマンド、または複数のファイルを標準出力に連結するコマンドを持つことが重要なのはなぜですか? 1つの理由は、標準入力で動作するすべてのUnixコマンドが少なくとも1つのコマンドラインファイル引数を解析し、存在する場合は入力として使用する方法を知ることを避けるためです。 2番目の理由は、ユーザーが覚えておく必要がないようにするためです。 (b)上記のサイレントパイプラインバグを回避します。
grep
に追加のロジックがある理由を理解できます。理論的根拠は、頻繁に使用されるコマンド(およびパイプラインとしてではなく)に基づいてstandaloneのユーザー流user性を許可することです。これは、使いやすさを大幅に向上させるための直交性のわずかな妥協です。すべてのコマンドをこのように設計する必要はありません。頻繁に使用しないコマンドは、ファイル引数の余分なロジックを完全に回避する必要があります(余分なロジックは不必要な脆弱性(バグの可能性)につながります)。例外は、grep
の場合のようにファイル引数を許可することです。 (ちなみに、ls
には、ファイル引数を受け入れるだけでなく、ファイル引数を必要とするというまったく異なる理由があることに注意してください)
最後に、ファイル引数が指定されているときに標準入力も使用できる場合、grep
(必ずしもls
である必要はありません)などの例外的なコマンドがエラーを生成する場合は、さらに改善できます。
いや!
まず、コマンドのどこでリダイレクトが発生するかは問題ではありません。したがって、コマンドの左側へのリダイレクトが必要な場合は、問題ありません。
< somefile command
と同じです
command < somefile
第二に、パイプを使用するときにn + 1プロセスとサブシェルが発生します。最も明らかに遅いです。場合によってはnがゼロになっていたため(たとえば、Shellビルトインにリダイレクトする場合)、cat
を使用することで、新しいプロセスを完全に不必要に追加することになります。
一般化として、パイプを使用していることに気付いたときは、パイプを削除できるかどうかを確認するのに30秒かかる価値があります。 (ただし、おそらく30秒より長くかかる価値はありません。)パイプとプロセスが不必要に頻繁に使用される例を次に示します。
for Word in $(cat somefile); … # for Word in $(<somefile); … (or better yet, while read < somefile)
grep something | awk stuff; # awk '/something/ stuff' (similar for sed)
echo something | command; # command <<< something (although echo would be necessary for pure POSIX)
サンプルを追加するには、自由に編集してください。
UUoCバージョンでは、cat
はファイルをメモリに読み込んでからパイプに書き出す必要があり、コマンドはパイプからデータを読み込む必要があるため、カーネルはファイル全体をコピーする必要があります- three timesリダイレクトされた場合、カーネルはファイルを一度コピーするだけです。 3回行うよりも1回行う方が速いです。
を使用して:
cat "$@" | command
cat
の完全に異なるものであり、必ずしも役に立たないわけではありません。コマンドが0個以上のファイル名引数を受け入れ、それらを順番に処理する標準フィルターである場合、それはまだ役に立ちません。 tr
コマンドを検討してください。これは、ファイル名の引数を無視または拒否する純粋なフィルターです。複数のファイルをフィードするには、示されているようにcat
を使用する必要があります。 (もちろん、tr
の設計はあまり良くないという別の議論があります。標準フィルターとして設計できなかった本当の理由はありません。)これは、コマンドにコマンドが複数の個別のファイルを受け入れる場合でも、すべての入力を複数の個別のファイルとしてではなく、単一のファイルとして扱います。たとえば、wc
はそのようなコマンドです。
cat single-file
無条件に役に立たないケース。
あまりにも独善的なUUOC賞のほとんどの例には同意しません。なぜなら、他の人に教えるとき、cat
は、議論されている問題やタスクに適した出力を生成するコマンドまたは無愛想な複雑なコマンドパイプラインの便利なプレースホルダーです。
これは、特にStack Overflow、ServerFault、Unix&Linux、または任意のSEサイトのようなサイトに当てはまります。
誰かが最適化について具体的に尋ねる場合、またはそれについて追加の情報を追加したい場合は、素晴らしい、猫の使用が非効率的である方法について話してください。しかし、人々をrateるな。彼らは、見た目だけでなく、見た目よりも単純さと理解しやすさを目指しているからだ!複雑。
要するに、猫は常に猫ではないからです。
また、UUOCの授与を楽しんでいるほとんどの人は、人々を助けたり教えたりすることよりも、自分がどれだけ「賢い」かを自慢することに関心があるため、UUOCの授与を楽しんでいます。現実には、彼らはおそらく彼らが仲間を打ち負かす小さな棒を見つけた単なる別の初心者であることを示しています。
更新
https://unix.stackexchange.com/a/301194/7696 の回答に投稿した別のUUOCを次に示します。
_sqlq() {
local filter
filter='cat'
# very primitive, use getopts for real option handling.
if [ "$1" == "--delete-blank-lines" ] ; then
filter='grep -v "^$"'
shift
fi
# each arg is piped into sqlplus as a separate command
printf "%s\n" "$@" | sqlplus -S sss/eee@sid | $filter
}
_
_$filter
_をデフォルトで空の文字列にし、_filter='| grep -v "^$"'
_にパイプ文字を埋め込まないことで、if
ステートメントに_$filter
_を実行させることが簡単にできるため、UUOCの指導者はUUOCであると言うでしょう。 、この「役に立たない」cat
は、printf
行の_$filter
_がsqlplus
への単なる別の引数ではなく、オプションのユーザー選択可能な出力フィルターであるという事実を自己文書化するという非常に便利な目的に役立ちます。
複数のオプションの出力フィルターが必要な場合、オプション処理は、必要に応じて_| whatever
_を_$filter
_に追加するだけです。パイプラインの余分なcat
は、何も傷つけたり、目立ったりすることはありません。パフォーマンスの損失。
defenseの猫:
はい、
< input process > output
または
process < input > output
より効率的ですが、多くの呼び出しにはパフォーマンスの問題がないため、気にしません。
左から右に読むのに慣れているので、次のようなコマンド
cat infile | process1 | process2 > outfile
理解するのは簡単です。
process1 < infile | process2 > outfile
process1を飛び越えてから、左から右に読む必要があります。これは次の方法で回復できます。
< infile process1 | process2 > outfile
なんとなく、左を指す矢印があるように見えます。より混乱し、派手な引用のように見える:
process1 > outfile < infile
多くの場合、スクリプトの生成は反復プロセスであり、
cat file
cat file | process1
cat file | process1 | process2
cat file | process1 | process2 > outfile
進捗状況を段階的に確認しながら、
< file
うまくいきません。簡単な方法ではエラーが発生しにくく、人間工学に基づいたコマンドの分類はcatで簡単です。
もう1つのトピックは、ほとんどの人が比較演算子として>および<にさらされており、コンピューターを使用するずっと前から、コンピューターをプログラマーとして使用しているときに、これらに頻繁にさらされることです。
また、2つのオペランドを<および>と比較すると、逆可換です。つまり、
(a > b) == (b < a)
入力リダイレクトに<を初めて使用したことを思い出しました。
a.sh < file
と同じ意味かもしれません
file > a.sh
そして、どういうわけか私のa.shスクリプトを上書きします。たぶん、これは多くの初心者にとっての問題です。
wc -c journal.txt
15666 journal.txt
cat journal.txt | wc -c
15666
後者は、計算で直接使用できます。
factor $(cat journal.txt | wc -c)
もちろん、ファイルパラメータの代わりに<もここで使用できます。
< journal.txt wc -c
15666
wc -c < journal.txt
15666
しかし、誰が気にします-15k?
たまに問題が発生した場合、猫を呼び出す習慣を変更することは確かです。
非常に大きなファイルまたは多数のファイルを使用する場合は、catを使用しないでください。ほとんどの質問では、catの使用は直交していて、トピックから外れており、問題ではありません。
シェルの2番目のトピックごとに猫の議論のこれらの役に立たない役に立たない使用を開始することは、単に迷惑で退屈です。パフォーマンスに関する質問に対処するときは、人生を得て、一分間の名声を待ってください。
追加の問題は、パイプがサブシェルを静かにマスクできることです。この例では、cat
をecho
に置き換えますが、同じ問題が存在します。
echo "foo" | while read line; do
x=$line
done
echo "$x"
x
にはfoo
が含まれていると思われるかもしれませんが、含まれていません。設定したx
は、while
ループを実行するために生成されたサブシェルにありました。パイプラインを開始したシェルのx
には無関係の値があるか、まったく設定されていません。
Bash4では、パイプラインの最後のコマンドがパイプラインを開始するシェルと同じシェルで実行されるようにいくつかのシェルオプションを構成できますが、これを試すことができます
echo "foo" | while read line; do
x=$line
done | awk '...'
x
はwhile
のサブシェルに対して再びローカルです。
定期的にこれと他の多くのシェルプログラミングアンチパターンを指摘している人として、遅かれ早かれ、重くのしかかる義務を感じます。
シェルスクリプトは非常にコピー/貼り付け言語です。シェルスクリプトを記述するほとんどの人々にとって、彼らは言語を学ぶためにそこにいません。それは、彼らが実際にある程度馴染みのある言語で物事を続けるために克服しなければならない単なる障害です。
その文脈では、さまざまなシェルスクリプトアンチパターンを広めることは破壊的であり、潜在的に破壊的であると考えています。誰かがStack Overflowで見つけたコードは、理想的には、最小限の変更と不完全な理解で環境にコピー/ペーストできるはずです。
ネット上の多くのシェルスクリプトリソースの中で、Stack Overflowは、ユーザーがサイトの質問と回答を編集することでサイトの品質を形作るのに役立つという点で珍しいです。ただし、 コードの編集には問題が生じる可能性があります コード作成者が意図していない変更を簡単に加えることができるためです。したがって、コードの変更を提案するコメントを残す傾向があります。
UUCAおよび関連するアンチパターンコメントは、コメントするコードの作成者だけのものではありません。それらは、サイトのreadersになるのを助けるcaveat emptorここで見つけたコードの問題を認識しています。
Stack Overflowで答えが役に立たないcat
s(または引用符で囲まれていない変数、またはchmod 777
、または他のさまざまなアンチパターンペスト)、しかし、少なくともこのコードを何百万回実行するスクリプトの最も内側のタイトなループにコピー/貼り付けしようとしているユーザーを教育するのに役立ちます。
技術的な理由に関する限り、従来の知恵は、外部プロセスの数を最小限に抑えようとすることです。これは、シェルスクリプトを作成する際の適切な一般的なガイダンスとして引き続き保持されます。
例ではcat file | myprogram
をよく使用します。いつか猫の無用な使用で告発されています( http://porkmail.org/era/unix/award.html )。次の理由で同意しません。
何が起こっているかを理解するのは簡単です。
UNIXコマンドを読み取る場合、コマンドの後に引数が続き、その後にリダイレクトが必要です。リダイレクションをどこにでも配置することは可能ですが、めったに見られません。そのため、例を読むのが難しくなります。私は信じている
cat foo | program1 -o option -b option | program2
より読みやすい
program1 -o option -b option < foo | program2
リダイレクトを最初に移動すると、この構文に慣れていない人を混乱させることになります。
< foo program1 -o option -b option | program2
そして例は理解しやすいものでなければなりません。
変更は簡単です。
プログラムがcat
から読み取れることがわかっている場合、通常、STDOUTに出力するプログラムから出力を読み取ることができると想定できるため、独自のニーズに合わせて予測可能な結果を得ることができます。
STDINがファイルでない場合、プログラムが失敗しないことを強調します。
program1 < foo
が機能する場合、cat foo | program1
も機能すると想定するのは安全ではありません。ただし、逆を想定しても安全です。このプログラムは、STDINがファイルの場合は機能しますが、入力がパイプの場合は失敗します。シークを使用するためです。
# works
< foo Perl -e 'seek(STDIN,1,1) || die;print <STDIN>'
# fails
cat foo | Perl -e 'seek(STDIN,1,1) || die;print <STDIN>'
追加のcat
を実行するにはコストがかかります。ベースライン(cat
)、低スループット(bzip2
)、中スループット(gzip
)、および高スループット(grep
)。
cat $ISO | cat
< $ISO cat
cat $ISO | bzip2
< $ISO | bzip2
cat $ISO | gzip
< $ISO gzip
cat $ISO | grep no_such_string
< $ISO grep no_such_string
テストは、ローエンドシステム(0.6 GHz)と通常のラップトップ(2.2 GHz)で実行されました。各システムで10回実行され、各テストの最適な状況を模倣するために最適なタイミングが選択されました。 $ ISOはubuntu-11.04-desktop-i386.isoでした。 (ここのきれいなテーブル: http://oletange.blogspot.com/2013/10/useless-use-of-cat.html )
CPU 0.6 GHz ARM
Command cat $ISO| <$ISO Diff Diff (pct)
Throughput \ Time (ms) User Sys Real User Sys Real User Sys Real User Sys Real
Baseline (cat) 55 14453 33090 23 6937 33126 32 7516 -36 239 208 99
Low (bzip2) 1945148 16094 1973754 1941727 5664 1959982 3420 10430 13772 100 284 100
Medium (gzip) 413914 13383 431812 407016 5477 416760 6898 7906 15052 101 244 103
High (grep no_such_string) 80656 15133 99049 79180 4336 86885 1476 10797 12164 101 349 114
CPU Core i7 2.2 GHz
Command cat $ISO| <$ISO Diff Diff (pct)
Throughput \ Time (ms) User Sys Real User Sys Real User Sys Real User Sys Real
Baseline (cat) 0 356 215 1 84 88 0 272 127 0 423 244
Low (bzip2) 136184 896 136765 136728 160 137131 -545 736 -366 99 560 99
Medium (gzip) 26564 788 26791 26332 108 26492 232 680 298 100 729 101
High (grep no_such_string) 264 392 483 216 84 304 48 308 179 122 466 158
結果は、低および中スループットの場合、コストは1%程度であることを示しています。これは測定の不確実性の範囲内であるため、実際には違いはありません。
高スループットの場合、差は大きくなり、2つの間に明確な違いがあります。
次の場合、<
の代わりにcat |
を使用する必要があります。
それ以外の場合は、<
を使用するかcat |
を使用するかは関係ありません。
したがって、次の場合にのみUUoC賞を授与する必要があります。