いくつかの気候データを含む7つのCSVファイルがあります。ファイル名は次のSMVV50065-2015-01.csv
とおりです*2015-02.csv
。2015-03.csv
csvファイルを開くと、次の構文が表示されます。
" SMVV, 2015-01-01 00:00,50065,780,7,1000,-2,18, , ,1000"
温度、圧力、湿度などの測定値を示します。 「、」は欠落しているデータを示します。 sedコマンドを使用して、欠落値をgapsからNAに変更しました。もっと具体的に書きました。
sed 's/ ,/NA/g' SMVV50065-2015-01.csv > newfile01.csv
すべてのスペースをNAに変更しました。問題は、foreachコマンドを使用して残りのファイルに対して同じことを行い、変更後に名前などを使用して新しいファイルに保存したいということです。このコマンドの正確な構文は何ですかnewfile01.csv
?newfile02.csv
答え1
CSVファイルにはカンマ付きの引用符がなく、改行付きのフィールドが厳密に含まれていないとします。
これにより、空のフィールドまたはスペースのみを含むフィールドが次のように変更されますNA
。
awk 'BEGIN { FS=OFS="," } { for (i=1;i<=NF;++i) if ($i ~ /^ *$/) $i = "NA"; print }'
各入力行のコンマ区切りフィールドごとに正規表現と一致するかどうかをテストします^ *$
。その場合、フィールドは文字列に置き換えられますNA
。ブロックの変数と変数は、FS
それぞれ入力フィールドと出力フィールドの区切り文字です。は現在の入力ラインで検出されたフィールドの数です。整数の場合は 1 から計算し、その整数に対応するフィールドになります。OFS
BEGIN
NF
awk
i
$i
あなたのサンプルライン、
SMVV, 2015-01-01 00:00,50065,780,7,1000,-2,18, , ,1000
なります
SMVV, 2015-01-01 00:00,50065,780,7,1000,-2,18,NA,NA,1000
すべてのファイルに対してこのコマンドを実行するには、そのファイルがすべてというディレクトリにあり、dir
ファイル名がパターンと一致すると仮定しますSMVV50065*.csv
。
このファイルをループするときの問題は次のとおりです。
for name in dir/SMVV50065*.csv; do
test -f "$name" || continue
# construct new name and call awk here
done
test -f
実際には通常のファイルかどうかをテストし$name
、そうでない場合は残りの繰り返しをスキップします。そうだろういいえパターンがディレクトリ名と一致する場合、またはパターンが一致しない場合何もない(この場合は拡張されていません)。
提案されたパターンに従って新しいファイル名を設定するには、一度から始めて各反復ごとに増加するカウンタ変数を保持し、printf
この変数ファイル名を使用して出力を提供する書式文字列として呼び出すことができます。
i=1
for name in dir/SMVV50065*.csv; do
test -f "$name" || continue
newname=$( printf 'newfile%02d.csv' "$i" )
i=$(( i + 1 ))
# call awk here
done
%02d
形式はprintf
、でゼロで埋められた2桁の整数を提供します$i
。
次にawk
、古いファイル名を呼び出し、結果を新しいファイルに書き込みます。元のファイルとは別のままにするために、結果をresult
ディレクトリのファイルに書き込みます。
#!/bin/sh
mkdir -p result
i=1
for name in dir/SMVV50065*.csv; do
test -f "$name" || continue
newname=$( printf 'newfile%02d.csv' "$i" )
i=$(( i + 1 ))
awk 'BEGIN { FS=OFS="," } { for (i=1;i<=NF;++i) if ($i ~ /^ *$/) $i = "NA"; print }' "$name" >result/"$newname"
done
ここで私がした唯一のことは、result
起動時にディレクトリが実際に存在したことを確認することでした。mkdir -p result
また、#!
これがスクリプトであることを示すために上部に1行を追加しましたsh
。
また、いくつかの診断とパラメータ化を追加します。
#!/bin/sh
indir=dir
outdir=result
mkdir -p "$outdir"
i=1
for name in "$indir"/SMVV50065*.csv; do
if [ ! -f "$name" ]; then
printf 'Not a regular file: "%s"\n' "$name" >&2
continue
fi
newname=$( printf '%s/newfile%02d.csv' "$outdir" "$i" )
i=$(( i + 1 ))
printf 'Processing "%s" into "%s"...\n' "$name" "$newname" >&2
awk 'BEGIN { FS=OFS="," } { for (i=1;i<=NF;++i) if ($i ~ /^ *$/) $i = "NA"; print }' "$name" >"$newname"
done
必要に応じてsed
、私のコマンドの代わりにここにコマンドを入力することもできます。awk
コメントの質問:
上記の作業は難しいようですが、なぜできませんか?
foreach file (ls SMVV50065-2015-0[1-7].csv)
sed 's/ ,/NA/g' > newfile0[1-7].csv
end
返信:
まず、正しい構文を使用して開始する必要があります。これはシェルの構文と多少似ているようですが、csh
質問には特定のシェルは言及されておらず、同様のシェルがよりsh
一般的に使用されるためそしてcsh
私はandの個人的な経験がほとんどないので、tcsh
これを構文に変換しますsh
。
sh
シェルのループはfor
whileで、角かっこの代わりにおよびをforeach
使用します。また、forループの使用を提案しましたが、厳密に言えば、対話型コマンドを使用すると、その結果は次のようになります。in
do
ls
ls
表示のみ(願いより」なぜ`ls`を解析しないのですか?"). ファイル名 グロービングパターンを使用すると、繰り返すファイル名のリストを生成するのに十分です。
それでは、正しい構文でループを試してみましょう。
for file in SMVV50065-2015-0[1-7].csv; do
sed 's/ ,/NA/g' > newfile0[1-7].csv
done
ここでループの次の問題は、それが有用$file
な値であるかどうかを単に知ることができないということです。パターンが SMVV50065-2015-0[1-7].csv
ディレクトリ名と一致するか、まったく一致しない場合は、パターンを使用しないでください$file
。
for file in SMVV50065-2015-0[1-7].csv; do
test -f "$file" || continue
sed 's/ ,/NA/g' > newfile0[1-7].csv
done
今すぐsed
呼び出します。いくつかのタスクを処理できるようにファイル名$file
を渡す必要があります。sed
for file in SMVV50065-2015-0[1-7].csv; do
test -f "$file" || continue
sed 's/ ,/NA/g' "$file" > newfile0[1-7].csv
done
次の問題は、実際には出力をsed
ファイル名globbingパターンにリダイレクトできないことですnewfile0[1-7].csv
。 globbing パターンは、シェルによってパターンと一致するすべての名前に展開されるか、一致しない場合は拡張されていません。状態。
現在のディレクトリにパターンnewfile0[1-7].csv
と一致するファイルがないとします。その後、ループはというファイルを生成しnewfile0[1-7].csv
、ループが繰り返されるたびに塗りつぶしを上書きします。
そのため、i
各反復ごとに新しいファイル名を設定できるように変数を導入しました。
i=1
for file in SMVV50065-2015-0[1-7].csv; do
test -f "$file" || continue
sed 's/ ,/NA/g' "$file" >"newfile0$i.csv"
i=$(( i + 1 ))
done
おそらく、処理するファイルが7つよりはるかに多いと思います。したがって、printf
ゼロで埋められた数字を含むファイル名を取得したことを確認するために、出力ファイル名の生成を使用していくつかの追加の問題が発生しました。
上記のループが役に立つかもしれませんが、少し書き直すと(新しいファイル名を変数に割り当てて、一緒に使用するsed
):
i=1
for file in SMVV50065-2015-0[1-7].csv; do
test -f "$file" || continue
newname="newfile0$i.csv"
i=$(( i + 1 ))
sed 's/ ,/NA/g' "$file" >"$newfile"
done
願いより?私たちはほとんど私の解決策に戻ってきました(最後のバリエーションの追加機能なし)。唯一の基本的な違いは、ここでは、すべてのファイルが現在のディレクトリで利用可能であり、出力ファイルを元のファイルと共に生成する必要があると仮定していることです。
答え2
以下は私が試したものです。
filnames.txt==>すべてのファイル名を含みます。
for j in `cat filenames.txt`; do sed "s/ ,/NA/g" $j >newfiles_$i;i=$(($i + 1)); done