ファイルでgrep
を2回実行せずに、1回のパスで変数を設定する方法はありますか?ファイルが小さいので、ワンパスでできるのかと思っていたので大したことではありません
FIRST_NAME=$(grep "$customer_id" customer-info|cut -f5 -d,)
LAST_NAME=$(grep "$customer_id" customer-info|cut -f6 -d,)
シェル文字列置換を使用して、1回grepし、2回分割できます。
_NAME=$(grep "$customer_id" customer-info | cut -f5,6 -d,)
FIRST_NAME=${NAME%,*}
LAST_NAME=${NAME#*,}
_
または、bashでプロセス置換を使用します。
_IFS=, read FIRST_NAME LAST_NAME < <(grep "$customer_id" customer-info | cut -f5,6 -d,)
_
read
はIFS
の入力を分割し、最初の値を_FIRST_NAME
_に割り当て、残りを_LAST_NAME
_に割り当てます。プロセスの置換とリダイレクト< <(...)
を使用すると、サブシェルを使用せずに_grep ... | cut ...
_の出力をread
に渡すことができます。
awk
をbash read
と組み合わせて使用できます。
read -r FIRST_NAME LAST_NAME <<< $(awk -F, -v cid="$customer_id" '$0~cid{print $5,$6}' customer-info)
-F
は、フィールド区切り文字としてコンマを使用するようにawkに指示します
-v
は、awk変数cid
をシェル変数$customer_id
に設定します
行が$customer_id
と一致する場合、awkは5番目と6番目のフィールドを出力し、これらには変数FIRST_NAME
およびLAST_NAME
が割り当てられます。
名($ 5)にスペースが含まれている場合(例:a、b、c、d、Sarah Jane、Smith)-v OFS=,
を追加して、フィールド間にawk
出力コンマを付け、read
の前にIFS=,
を付けてくださいカンマで分割します。
さらに、awk
は'$3~cid{print..}'
のような特定のフィールドのみを検索でき、IDが重要な場合は'$3~"^"cid"$"{print...}'
によってそのentireフィールドと一致できます。
最も簡単なのは、レコード全体を変数に入れて、その上でcut
を使用することです。
RECORD=$(grep "$customer_id" customer-info)
FIRST_NAME=$(echo "$RECORD"|cut -f5 -d,)
LAST_NAME=$(echo "$RECORD"|cut -f6 -d,)
また、個人的には、より具体的な正規表現を使用することをお勧めします。カスタマーIDが常に行の先頭にある場合は、grep '^'"$customer_id"
の代わりに grep "$customer_id"
は、一致が行の先頭にあることを要求します。それ以外の場合は、顧客IDと一致するテキストがレコードの別の場所に偶然出現するレコードを選択する可能性があります。
小さなファイル、大きなファイル。私の習慣の1つは、常にディスクを削除することですIOできる限り多くのことです。これを行う1つの方法は、ファイルを配列にプッシュすることです。もちろん、これにはenv $ IFSが適切に設定されている必要がありますファイルの場合はIOを排除します。
data=( $(cat customer-info) )
それからチェリーピックできます...
FIRST_NAME=$(echo "${data[@]}" | tr ' ' '\n' | grep "$customer_id" | cut -f5 -d,)
別の方法は、そのような配列に必要なこれらの2ビットだけを割り当てることかもしれません...
data=( $(grep "${customer_id}" customer-info | cut -d, -f5,6) )
既存の回答はすべてメモリ(変数)に出力を格納し、2回再生します。これは、任意に大きな入力を取り、2つのタスクを実行できる汎用ラッパーを作成する場合の問題です。代わりに、出力ストリームを複製して、2つのコマンドにストリーミングできます。
私の場合、目的は、任意に長くなる可能性がある出力ストリームのヘッダー(最初の行)と特定の(セットの)行の両方をフィルタリングすることです。簡単な例は、ディスク領域の使用状況を表示することです。
_$ df -h | tee >(head -1 >&2) | grep '/$'
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 202G 145G 57G 72% /
_
_df -h
_を使用するコマンドに置き換え、_head -1
_および_grep '/$'
_をそれに適用する2つのコマンドに置き換えます。両方の出力が端末に表示されますが、前者のコマンドの出力が後者の後に表示される場合もあります。
これはどのように作動しますか?
tee
"各[引数]と標準出力への[コピー]標準入力。"したがって、_command | tee /dev/stderr
_を使用して、stdinの出力をstdoutとstderrの両方に送信できます。command >(command2)
構文はbashによって引数に置き換えられるため、_command /dev/fd/63
_が実行されます。 command
が_/dev/fd/63
_に書き込もうとすると、_command2
_の入力(stdin)になります。これはプロセス置換と呼ばれます(_man bash
_を参照)。tee
は引数(コマンド置換を引数として渡す)とstdoutの両方に書き込むため、別のパイプを追加して別のコマンドを実行できます。これでcommand | tee >(command2) | command3
ができました。command3
_にパイプされるため、(この例では)ヘッダー行をgrepすることになります。それは私たちが望むものではありません。それを表示したいのです。 stderrをパイピングしないので、出力をstderrにリダイレクトすることは、ターミナルに表示する簡単な方法です。つまり、_>&2
_を追加して、command | tee >(command2 >&2) | command3
を生成します。問題が1つあります。出力の順序は任意です。宇宙線に応じて、上記または次のいずれかが表示される場合があります。
_$ df -h | tee >(head -1 >&2) | grep '/$'
/dev/sda1 202G 145G 57G 72% /
Filesystem Size Used Avail Use% Mounted on
_
これを修正するためのハッキーですが信頼性の高い方法(ハックではない過度に設計された方法の代わり)は、2番目のコマンドに短いスリープを追加することです。何かのようなもの:
_$ df -h | tee >(head -1 >&2) | sleep 1; grep '/$'
_
しかし、待ってください、これは2番目のコマンド(grep
)を壊します。これは、出力がtee
からsleep
にパイプされ、grep
が入力を無期限に待機するためです。 。これを修正するには、サブシェルを追加します。
_$ df -h | tee >(head -1 >&2) | (sleep 0.01; grep '/$')
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 202G 145G 57G 72% /
_
これで、出力はgrep
にリダイレクトされず、サブシェルにリダイレクトされます。 sleep
はそこから読み取らない(ストリームを消費しない)ため、grep
は引き続き読み取ることができます。現在は、0.01秒以内にhead
が出力する限り(およびgrep側でのオーバーヘッドが少し)、確実に機能します。これは、最近のシステムではかなりの賭けであり、ユーザーが気付かないほど短い時間です。
ヘッダーとコマンドの出力の両方を取得するものを作成したかったので、これを一般化して次のようにできます。
_function grabheader {
tee >(head -1 >&2)
}
_
関数内のtee
コマンドはstdinから読み取ってstdoutに出力するだけなので、これを_df -h | grabheader | grep '/$'
_として使用すると、以前の順序が狂ったコマンドと同じになります。しかし、それを順序どおりにしたいので、標準出力への送信を遅らせる必要があります。
_function grabheader {
tee >(head -1 >&2) | (sleep 0.01; cat)
}
_
cat
ここでは、stdinに渡されたものがすべてstdoutに再び送られることを確認しています。引数を渡さず、リダイレクトを追加しないことで、それはまさにそれを行います。使用法:
_$ df -h | grabheader | grep '/$'
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 202G 145G 57G 72% /
_
もちろん、df
の特定のケースでは、これははるかに簡単に行うことができます。
_$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 202G 145G 57G 72% /
_
しかし、今では、どのコマンドでもこれを行う一般的な方法があります。