一貫した形式の一連のテキストファイルがあります。たとえば、次のようになります。
FirstName: Mary
LastName: Smith
Address: 123 Anywhere St
City: Nowhere
State: TX
Zip: 77777
このファイルから数行を抽出し、次の形式のcsvファイルに出力する必要があります。
<filename>,<FirstName>,<City>,<Zip>
単純なgrepを使って目的のフィールドを取得できますが、必要な方法で出力を取得する方法がわかりません。
答え1
ファイルごとに1つのレコードしかない場合、これは単純な読み取りループです。
#!/bin/bash
read_data()
{
local first last addr city state zip
file=$1
while read -r header data
do
case $header in
FirstName:) first=$data ;;
LastName:) last=$data ;;
Address:) addr=$data ;;
City:) city=$data ;;
State:) state=$data ;;
Zip:) zip=$data ;;
*) echo Ignoring bad line $header $data >&2
esac
done < $file
echo "$file,$first,$last,$addr,$city,$state,$zip"
}
for file in *srcfiles*
do
read_data $file
done
このread_data
関数は各行を読み取り、行を「ヘッダー」と「データ」に分割します。ファイルの終わりに達すると結果を印刷します。
ループを介して各ソースファイルに対してこの関数を一度に呼び出しますfor
。
いくつかの潜在的な問題に注意してください。データにカンマがあると問題が発生するため、次のことができます。
echo "\"$file\",\"$first\",\"$last\",\"$addr\",\"$city\",\"$state\",\"$zip\""
"..."
レイアウト内に含まれるすべての内容を出力として使用します。これにより、"
データに内容があるとCSV形式が正しくない可能性があります。
echo
目的のフォーマットに合わせて線を調整してください。
答え2
お客様のニーズに合わせた迅速で汚いアプローチです。
grep . *|perl -ne 'if(/FirstName: (.*)/){$f=$1}if(/City: (.*)/){$c=$1}if(/^(.*):Zip: (.*)/){print "$1,$f,$c,$2\n"}'
例:
grep . *
f1.txt:FirstName: Mary
f1.txt:LastName: Smith
f1.txt:Address: 123 Anywhere St
f1.txt:City: Nowhere
f1.txt:State: TX
f1.txt:Zip: 77777
f2.txt:FirstName: Joe
f2.txt:LastName: Bloggs
f2.txt:Address: 444 Anywhere St
f2.txt:City: Nowhere2
f2.txt:State: TXA
f2.txt:Zip: 77737
grep . *|perl -ne 'if(/FirstName: (.*)/){$f=$1}if(/City: (.*)/){$c=$1}if(/^(.*):Zip: (.*)/){print "$1,$f,$c,$2\n"}'
f1.txt,Mary,Nowhere,77777
f2.txt,Joe,Nowhere2,77737
答え3
ファイルごとにレコードが1つだけでGNU awkがある場合は、次のことができます。
gawk -F': +' -vOFS=, '
BEGINFILE{delete rec}
{rec[$1] = $2}
ENDFILE{print FILENAME, rec["FirstName"], rec["City"], rec["Zip"]}
' file1.txt file2.txt ...