警告:完全初心者です。 .csvファイルに列を追加する必要があります。ここでは、列ヘッダーは「名前」にすることができますが、列全体は、ファイル自体の名前、.csvファイルの名前など、まったく同じでなければなりませんfilename
。これでファイルごとに3つの変数しかありませんが、2100行になります。
例: ファイルの場合"bcc1_45Fall_10010002.csv"これが私が持っているものです -
HUC8 YEAR RO_MM
10010002 1961 74.7
10010002 1962 69.1
10010002 1963 52.0
10010002 1964 130.7
10010002 1965 32.2
10010002 1966 85.4
これが私が望むものです -
NAME HUC8 YEAR RO_MM
bcc1_45Fall_10010002 10010002 1961 74.7
bcc1_45Fall_10010002 10010002 1962 69.1
bcc1_45Fall_10010002 10010002 1963 52.0
bcc1_45Fall_10010002 10010002 1964 130.7
bcc1_45Fall_10010002 10010002 1965 32.2
bcc1_45Fall_10010002 10010002 1966 85.4
またはこれ -
HUC8 YEAR RO_MM
bcc1_45Fall_10010002 1961 74.7
bcc1_45Fall_10010002 1962 69.1
bcc1_45Fall_10010002 1963 52.0
bcc1_45Fall_10010002 1964 130.7
bcc1_45Fall_10010002 1965 32.2
bcc1_45Fall_10010002 1966 85.4
「HUC8」列のすべてのデータを簡単に置き換えることができればfilename
完璧です。追加の列である必要はありません。
何千ものファイルに対してこれを行う必要があります。
最初の部分を実行する方法を知っている場合は、ループを作成できます。しかし、もっと良い方法がありますか?
どこから始めるべきかわかりません。
答え1
使用awk
とcolumn
:
$ awk '
NR==1{ sub(/\.csv$/, "", FILENAME) } # remove .csv suffix from FILENAME
NR>1{ $1=FILENAME } # replace the first field with filename
1 # print record
' bcc1_45Fall_10010002.csv | column -t
HUC8 YEAR RO_MM
bcc1_45Fall_10010002 1961 74.7
bcc1_45Fall_10010002 1962 69.1
bcc1_45Fall_10010002 1963 52.0
bcc1_45Fall_10010002 1964 130.7
bcc1_45Fall_10010002 1965 32.2
bcc1_45Fall_10010002 1966 85.4
シェルループでこのコマンドを実行して、変更したファイルをディレクトリに保存できますmodified_files
。
mkdir modified_files &&
for i in *.csv; do
awk 'NR==1{ sub(/\.csv$/, "", FILENAME) } NR>1{ $1=FILENAME }1' "$i" |
column -t > "./modified_files/$i"
done
列を置き換える必要がありますが、HUC8
これが最初の列でない場合は、コードを次のように変更します。
awk -v search='HUC8' '
NR==1{
for(i=1;i<=NF;i++)
if ($i==search){ fld=i; sub(/\.csv$/, "", FILENAME); break }
}
NR>1{ $fld=FILENAME }
1
' file.csv | column -t
答え2
使用ミラー、ファイルが「単純な」CSV(カンマなし)であるとします。以内にフィールドなど - 完全なRFC-4180サポートが必要な場合はそれを変更できます--csvlite
。--csv
$ cat bcc1_45Fall_10010002.csv
HUC8,YEAR,RO_MM
10010002,1961,74.7
10010002,1962,69.1
10010002,1963,52.0
10010002,1964,130.7
10010002,1965,32.2
10010002,1966,85.4
それから
現在
HUC8
の列を変更します。$ mlr --csvlite put -S '$HUC8 = substr(FILENAME,0,-5)' bcc1_45Fall_10010002.csv HUC8,YEAR,RO_MM bcc1_45Fall_10010002,1961,74.7 bcc1_45Fall_10010002,1962,69.1 bcc1_45Fall_10010002,1963,52.0 bcc1_45Fall_10010002,1964,130.7 bcc1_45Fall_10010002,1965,32.2 bcc1_45Fall_10010002,1966,85.4
別の
Name
列を追加します。$ mlr --csvlite put -S '$Name = substr(FILENAME,0,-5)' bcc1_45Fall_10010002.csv HUC8,YEAR,RO_MM,Name 10010002,1961,74.7,bcc1_45Fall_10010002 10010002,1962,69.1,bcc1_45Fall_10010002 10010002,1963,52.0,bcc1_45Fall_10010002 10010002,1964,130.7,bcc1_45Fall_10010002 10010002,1965,32.2,bcc1_45Fall_10010002 10010002,1966,85.4,bcc1_45Fall_10010002
Name
列を最初の列として追加します。$ mlr --csvlite put -S '$Name = substr(FILENAME,0,-5)' then reorder -f Name bcc1_45Fall_10010002.csv Name,HUC8,YEAR,RO_MM bcc1_45Fall_10010002,10010002,1961,74.7 bcc1_45Fall_10010002,10010002,1962,69.1 bcc1_45Fall_10010002,10010002,1963,52.0 bcc1_45Fall_10010002,10010002,1964,130.7 bcc1_45Fall_10010002,10010002,1965,32.2 bcc1_45Fall_10010002,10010002,1966,85.4
上記のすべては、結果を標準出力に記録します。ファイルを変更するには、この-I
オプションを追加してください。 Shell glob exを使用すると、一度に複数のファイルを渡すことができます。bcc*.csv
または*.csv
。
[テストするときいいえ -I
レコードの不均一性のために新しいヘッダーが必要でない限り、ヘッダー行は繰り返されません。ただし、-I
各ファイルには適切なヘッダーが追加されます。 ]
答え3
$ perl -lne 'BEGIN {$fnr=1};
if ($fnr == 1) {
($fn = $ARGV) =~ s/\.[^.]+$//;
print "NAME,$_"
} else {
print "$fn,$_"
};
$fnr++;
if (eof) {$fnr=1}' *.csv
これにより、ファイル名(.csv「拡張子」を除く)が最初のフィールドとして追加され、.csvファイルの内容が標準出力に印刷されます。
awk
とは異なり、perl
各個々のファイルの行数を追跡しません(変数のある行の総数のみを追跡します$.
)。スクリプトはまずBEGINブロックに変数を設定し、$fnr
読み取った各行に対して変数をインクリメントし、最後にファイルの終わりに達するたびに再び1にリセットして、この数を手動で維持します。
ファイル名を最初のフィールドではなく最後のフィールドとして追加するように簡単に変更されます。たとえば、print
2 つの文を次のように変更します。
print "$_,NAME"
and:
print "$_,$fn"
最初のフィールド以外の行の他の場所にファイル名フィールドを挿入する必要がある場合は、Perlのsplice
機能を使用できます。
たとえば、次はファイル名を3番目のフィールドとして挿入します(perl配列のインデックスは1ではなく0から始まるため、3番目のフィールドは$F[2]
代わりになります$F[3]
)。
$ perl -F, -lne 'BEGIN {$fnr=1; $field_num=2};
if ($fnr == 1) {
($fn = $ARGV) =~ s/\.[^.]+$//;
splice @F, $field_num, 0, "NAME";
} else {
splice @F, $field_num, 0, $fn;
};
print join(",", @F);
$fnr++;
if (eof) {$fnr=1}' *.csv
これはPerlの-F
オプションを使用してカンマをフィールド区切り文字に設定します。また、Perlの自動分割機能を使用して、入力行を名前付き配列に自動分割することもできます@F
(これは、入力行を$ 1、$ 2、$ 3などに自動的に分割するawkのデフォルトの動作と似ています)。リテラル文字列 "NAME" または変更されたファイル名を @F で連結し、@F
コンマ文字で連結された配列要素を印刷します。
最後に、ファイルの内容を実際に変更するには、Perlの-i
オプションを使用します。-i
名前の変更などのオプションとともに、「拡張子」を使用して元のファイルのバックアップを保持することを選択できますfilename.csv
。filename.csv.orig
-iorig
perl -iorig -lne '......' *.csv
または
perl -iorig -F, -lne '......' *.csv
答え4
次に、awkを使用してファイル名を繰り返し、列を印刷します。
for f in *.csv;
do
head -1 $f > out/$f
cat $f | awk -v FIN=${f%.csv} 'NR>1 {print FIN, $2, $3}' >> out/$f
done
HUC8 YEAR RO_MM
bcc1_45Fall_10010002 1961 74.7
(...)