UNIX foreachとsedコマンド!

UNIX foreachとsedコマンド!

いくつかの気候データを含む7つのCSVファイルがあります。ファイル名は次のSMVV50065-2015-01.csvとおりです*2015-02.csv2015-03.csvcsvファイルを開くと、次の構文が表示されます。

" 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.csvnewfile02.csv

答え1

CSVファイルにはカンマ付きの引用符がなく、改行付きのフィールドが厳密に含まれていないとします。

これにより、空のフィールドまたはスペースのみを含むフィールドが次のように変更されますNA

awk 'BEGIN { FS=OFS="," } { for (i=1;i<=NF;++i) if ($i ~ /^ *$/) $i = "NA"; print }'

各入力行のコンマ区切りフィールドごとに正規表現と一致するかどうかをテストします^ *$。その場合、フィールドは文字列に置き換えられますNA。ブロックの変数と変数は、FSそれぞれ入力フィールドと出力フィールドの区切り文字です。は現在の入力ラインで検出されたフィールドの数です。整数の場合は 1 から計算し、その整数に対応するフィールドになります。OFSBEGINNFawki$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シェルのループはforwhileで、角かっこの代わりにおよびをforeach使用します。また、forループの使用を提案しましたが、厳密に言えば、対話型コマンドを使用すると、その結果は次のようになります。indolsls表示のみ(願いより」なぜ`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

関連情報