%20%E2%80%94%20%D0%B4%D0%BB%D1%8F%20%D0%BD%D0%B5%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D0%B8%D1%85%20%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%20%D0%B2%20BaSH.png)
Предостережение: Абсолютный новичок. Мне нужно добавить столбец в файл .csv, где заголовок столбца может быть "Name", но весь столбец должен быть точно таким же — имя самого файла, 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 (без запятыхв пределахполя и т. д. - можно изменить --csvlite
на , --csv
если требуется полная поддержка RFC-4180)
$ 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 globs, например, 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
не отслеживает количество строк для каждого отдельного файла (отслеживает только общее количество строк с $.
переменной). Этот скрипт поддерживает это количество вручную, сначала устанавливая переменную $fnr
в блоке BEGIN, затем увеличивая ее для каждой прочитанной строки и, наконец, сбрасывая ее обратно на 1 каждый раз, когда достигается конец файла.
Это легко изменить, добавив имя файла в качестве последнего поля вместо первого. Например, измените два print
оператора на:
print "$_,NAME"
and:
print "$_,$fn"
Если вам нужно вставить поле имени файла в другое место строки, а не в первое поле, вы можете использовать splice
функцию Perl.
Например, следующий код вставляет имя файла в качестве третьего поля (обратите внимание, что индексы массива Perl начинаются с нуля, а не с 1, поэтому третье поле — $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
(это похоже на поведение awk по умолчанию, когда входная строка автоматически разбивается на $1, $2, $3 и т. д.). Либо литеральная строка "NAME", либо измененное имя файла объединяются в @F, затем элементы массива @F
выводятся, соединяясь запятыми.
Наконец, если вы хотите изменить содержимое файлов, используйте -i
параметр perl. Вы можете по желанию сохранить резервную копию исходного файла, используя "расширение" с параметром -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
(...)