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を1回使用して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
答え2
最も簡単な方法は、レコード全体を変数に入れて使用する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 に一致するテキストがレコードの他の場所に表示されるレコードを選択できます。
答え3
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
sumが割り当てられますLAST_NAME
。
名前($ 5)にスペース(たとえば、a、b、c、d、Sarah Jane、Smith)が含まれている場合は、フィールド間に-v OFS=,
出力コンマを追加し、awk
プレフィックスread
としてwithを追加IFS=,
してコンマで区切るようにします。
また -awk
などの特定のフィールド内でのみ検索が可能で、'$3~cid{print..}'
そのフィールドと一致します。みんな'$3~"^"cid"$"{print...}'
あなたのIDにとって重要な場合はフィールドです。
答え4
既存の答えはすべて、出力をメモリ(変数)に保存して2回再生します。ランダムに大きな入力を受け入れ、これに対して2つのことができる一般的なラッパーを作成しようとすると、これは問題になります。代わりに、出力ストリームをコピーして両方のコマンドにストリーミングできます。
私の場合、目的は出力ストリームから任意に長くできるヘッダー(最初の行)と特定の行セットをフィルタリングすることです。簡単な例は、ディスク容量の使用量を表示することです。
$ df -h | tee >(head -1 >&2) | grep '/$'
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 202G 145G 57G 72% /
df -h
使用したいコマンドに置き換えて、適用したい2つのコマンドにhead -1
置き換えます。grep '/$'
両方の出力が端末に表示されますが、電子コマンドの出力は後者の後に表示されます。
どのように動作しますか?
- プログラムは
tee
「各[引数]に標準入力をコピーし、標準出力にもコピーします。」したがって、を使用してstdinの出力をstdoutおよびstderrに送信できますcommand | tee /dev/stderr
。 - この
command >(command2)
構文は bash の引数に置き換えられるのでcommand /dev/fd/63
実行されます。command
を書き込もうとすると、/dev/fd/63
の入力(stdin)で終了しますcommand2
。これをプロセス置換と呼びます(参考資料を参照man bash
)。 tee
引数(コマンド置換を引数として渡す)と標準出力が同時に作成されるため、異なるパイプを追加して別のコマンドを実行できます。今私たちはそれを持っていますcommand | tee >(command2) | command3
。- 最後に、 command2 は stdout として出力し、stdout は にパイプされるので
command3
(私の例では) ヘッダラインを grep します。それは私たちが望むものではありません。私たちはそれを見せたいです。 stderrをパイプしないので、出力をstderrにリダイレクトすることが端末に表示される簡単な方法です。つまり、 を追加すると>&2
結果は ですcommand | tee >(command2 >&2) | command3
。
問題があります。出力順序は任意です。宇宙船によっては、上または次を見ることができます。
$ 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 '/$'
grep
しかし、ちょっと待ってください。これでtee
、出力がからパイプに入り、sleep
入力grep
を無期限に待機するため、2番目のコマンド()が中断されます。この問題を解決するためにサブシェルを追加します。
$ 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
。head
0.01秒(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
これにより、標準入力に渡されたすべての項目が標準出力に戻ります。パラメータを渡さずにリダイレクトを追加しないことでこれを行います。使用法:
$ 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% /
しかし今、私たちはどんなコマンドでもこれを行うための一般的な方法を持っています。