多くのコマンドラインユーティリティは、パイプまたはファイル名引数から入力を受け取ることができます。長いシェルスクリプトの場合、チェーンを起動するとcat
読みやすくなります。特に、最初のコマンドに複数行の引数が必要な場合はさらにそうです。
比較する
sed s/bla/blaha/ data \
| grep blah \
| grep -n babla
そして
cat data \
| sed s/bla/blaha/ \
| grep blah \
| grep -n babla
後者のアプローチはそれほど効率的ではありませんか?それでは、スクリプトを実行するかどうか(たとえば、1秒に1回)を気にするほど違いはありますか?読みやすさの違いは大きくありません。
答え1
もちろん、「最終」の答えは次のとおりです。cat
賞の無駄な使用。
catの目的は、ファイルをリンク(または「接続」)することです。単純なファイルの場合、他のものとリンクするのは時間の無駄であり、プロセスのコストがかかります。
コードを読みやすくするためにcatをインスタンス化すると、プロセスと不要な入力/出力ストリームセットのみが追加されます。多くの場合、スクリプトの実際の障害は、非効率的な屋根ふきと実際の処理です。ほとんどの最新システムでは、追加の方法はパフォーマンスcat
に影響を与えませんが、ほとんど常にコードを書く他の方法があります。
すでに知っているように、ほとんどのプログラムは入力ファイルの引数を受け入れることができます。ただし、STDINストリームが必要なときはいつでも、すでに<
実行されているシェルプロセスでタスクを実行してプロセスを保存する組み込みシェルを使用することは常に可能です。
書く場所に応じて創造性を発揮することもできます。通常、次のように出力リダイレクトまたはパイプが指定される前にコマンドの最後に配置されます。
sed s/blah/blaha/ < data | pipe
しかし、必ずしもそうではありません。最初に来ることもできます。たとえば、サンプルコードは次のように書くことができます。
< data \
sed s/bla/blaha/ |
grep blah |
grep -n babla
スクリプトの読みやすさを重視し、コードが複雑すぎて行を追加すると、理解しcat
やすくなると思われる場合は、コードを整理する別の方法があります。私がよく使う1つの方法は、パイプラインを論理セットに分割して関数に格納することです。これにより、後でスクリプトを理解しやすくなります。これにより、スクリプトコードが非常に自然になり、パイプラインのすべての部分をデバッグするのが簡単になります。
function fix_blahs () {
sed s/bla/blaha/ |
grep blah |
grep -n babla
}
fix_blahs < data
その後、続行できますfix_blahs < data | fix_frogs | reorder | format_for_sql
。これらのパイプラインは本当に理解しやすく、個々のコンポーネントはその機能で簡単にデバッグできます。
答え2
以下は、いくつかの欠点を要約したものです。
cat $file | cmd
超過
< $file cmd
まず、注:上記の二重引用符がありません(意図的にこの議論の目的のために)
$file
。リダイレクトの場合、cat
これは常に問題ですzsh
。リダイレクトの場合bash
やPOSIXモードを含む他の一部のシェルでは、スクリプトではなく対話型でのみ問題が発生しますksh88
。bash
最も一般的に言及される欠点は、追加のプロセスが作成されることです。
cmd
一部のシェルでは、組み込みの場合、2つのプロセスも作成されますbash
。それでもパフォーマンスの観点からは、
cat
組み込みシェルに加えて(もちろんロードおよび初期化(およびリンクされているライブラリ)と共に)追加のコマンドが実行されます。パフォーマンスの観点から見ると、これは大容量ファイルの場合、システムがスケジュールとプロセスを交互
cat
にcmd
し、パイプバッファを継続的に埋め、空にする必要があることを意味します。大規模なシステムコールをcmd
実行しても、パイプは一度に数キロバイトを超えるデータを保持できないため、制御を前後に切り替える必要があります。1GB
read()
cat
cmd
cmd
一部のs(例えば)は、標準入力が通常のファイルであるときにいくつかの最適化を実行できますが、標準入力はパイプであるため、wc -c
そうすることはできません。パイプcat | cmd
の場合、cat
これはseek()
ファイルに含めることができないことを意味します。tac
あるいは、このようなコマンドでは、入力全体をメモリに保存する必要があるため、tail
パフォーマンスに大きな違いがあります。cat
cat $file
、さらに正確なバージョンであっても、一部の特定のファイル名(または次に始まるファイル名を忘れた場合)ではcat -- "$file"
正しく機能しません。誰かがそれを使用しようとすると、信頼性を確保するためにおそらくそうする必要があります。-
--help
-
--
cat
cat < "$file" | cmd
読み取り用に開くことができない場合
$file
(アクセス拒否、存在しない...)、< "$file" cmd
一貫したエラーメッセージが報告され(シェルで)いいえruncmd
、whileはcat $file | cmd
まだruncmd
ですが、標準入力は空のファイルのように見えます。これはまた、このような場合に< file cmd > file2
開けないとfile2
破壊されないことを意味します。file
つまり、
cmd file > file2
出力ファイルが常に(シェルを介して)開く順序ではなく、入力ファイルと出力ファイルが開く順序を選択できます。今後入力ファイル(bycmd
)を使用する場合、これはほとんど望ましくありません。cmd1 < file | cmd2 > file2
ただし、場所cmd1
と場所、およびリダイレクトを同時に独立して実行するのには役立たず、開けないときに壊れたり実行されたりするのを防ぐために、または作成する必要がありますcmd2
。{ cmd1 | cmd2; } < file > file2
(cmd1 | cmd2 > file2) < file
file2
cmd1
cmd2
file
答え3
<file
パイプの端に配置するのは、cat file
最初に配置するよりも読みにくいです。自然な英語は左から右に読みます。
<file
パイプの先頭を最初に置くのもcatより読みやすくなると言いたいです。単語は、記号、特に間違った方向を指すように見える記号よりも読みやすくなります。
cat
保存された書式を使用しますcommand | command | command
。
答え4
ここで他の答えが直接解決できないことの1つは、「cat
不要な作業を実行する猫プロセスを作成する」ため、「機能しない無関係な猫プロセスを生成する」ため、このように使用することは「役に立たない」ことですです。 " "意味、それは役に立たない。
どちらの場合も:
sed 's/foo/bar/' somefile
<somefile sed 's/foo/bar/'
シェルは、いくつかのファイルまたは標準入力(それぞれ)から読み取られるsedプロセスを開始し、いくつかの処理を実行します。改行文字に出会うまで読み、行の最初の「foo」を「bar」(存在する場合)に置き換えます。その後、その行を標準出力に印刷して繰り返します。
次の場合:
cat somefile | sed 's/foo/bar/'
シェルはcatプロセスとsedプロセスを生成し、catの標準出力をsedの標準入力に接続します。 catプロセスはファイルからキロバイトまたはメガバイト単位のチャンクを読み取り、それを標準出力に書き込みます。上記の2番目の例に示すように、sed sommandはそれを取得します。 sedがこのブロックを処理している間、catはsedが続行できるように他のブロックを読み取り、それを標準出力に書き込みます。
つまり、コマンドを追加するために必要な追加のタスクは、追加プロセスを作成する追加のcat
タスクだけでなく、cat
ファイルバイトを1回ではなく2回読み書きする追加のタスクです。現実的に言えば、現代のシステムではこれは大きな違いをもたらしません。システムに数マイクロ秒間不要な操作を実行させることもできます。ただし、既にパフォーマンスが不足しているコンピュータでスクリプトを使用している人にスクリプトを配布したい場合は、複数の反復で数マイクロ秒が経過する可能性があります。