
注意: まったくの初心者です。.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
) を使用して、複数のファイルを一度に渡すことができます。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 ファイルの内容が stdout に出力されます。
とは異なりawk
、perl
は各ファイルの行数を追跡しません (変数を使用して合計行数のみを追跡します$.
)。このスクリプトは、最初に$fnr
BEGIN ブロックで変数を設定し、次に読み取られる行ごとに変数を増分し、最後にファイルの末尾に到達するたびに変数を 1 にリセットすることで、手動で行数を維持します。
これを変更して、ファイル名を最初のフィールドではなく最後のフィールドとして追加することは簡単です。たとえば、2 つのprint
ステートメントを次のように変更します。
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
これは、-F
フィールド区切り文字としてコンマを設定する perl のオプションを使用します。これにより、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
(...)