%20-%20para%20v%C3%A1rios%20arquivos%20no%20BaSH.png)
Advertência: Iniciante absoluto. Preciso adicionar uma coluna a um arquivo .csv onde o cabeçalho da coluna pode ser "Nome", mas a coluna inteira deve ser exatamente a mesma - o nome do arquivo em si, a extensão filename
. Cada arquivo tem apenas 3 variáveis agora, mas 2.100 linhas.
Exemplo: Para arquivo"bcc1_45Fall_10010002.csv"isso é o que eu tenho -
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
É isso que eu quero -
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
Ou isto -
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
Se eu pudesse simplesmente substituir todos os dados da coluna "HUC8" por isso filename
seria perfeito. Não precisa ser uma coluna extra.
Preciso fazer isso para muitos milhares de arquivos.
Se eu soubesse fazer a primeira parte, poderia criar um loop. Mas talvez haja uma maneira ainda melhor?
Não sei por onde começar.
Responder1
Usando awk
e 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
Você pode executar isso em um loop de shell para salvar os arquivos modificados no diretório 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
Se você precisar substituir a coluna HUC8
e esta não for a primeira coluna, altere o código para isto:
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
Responder2
UsandoMoleiro, e assumindo que seus arquivos sejam CSV "simples" (sem vírgulasdentro decampos e assim por diante - você pode alterar --csvlite
para --csv
se for necessário suporte completo a 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
então
para substituir a
HUC8
coluna atual:$ 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
para adicionar uma
Name
coluna separada:$ 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
para adicionar uma
Name
coluna como a primeira coluna:$ 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
Todos os itens acima gravam o resultado na saída padrão - para modificar o arquivo no local, adicione a -I
opção. Você pode passar vários arquivos de uma vez usando shell globs ex. bcc*.csv
ou *.csv
.
[Ao testarsem -I
a linha do cabeçalho não será repetida, a menos que um novo cabeçalho seja necessário como resultado da heterogeneidade do registro; no entanto, -I
um cabeçalho apropriado será adicionado a cada arquivo.]
Responder3
$ perl -lne 'BEGIN {$fnr=1};
if ($fnr == 1) {
($fn = $ARGV) =~ s/\.[^.]+$//;
print "NAME,$_"
} else {
print "$fn,$_"
};
$fnr++;
if (eof) {$fnr=1}' *.csv
Isso adicionará o nome do arquivo (sem a "extensão" .csv) como o primeiro campo e imprimirá o conteúdo dos arquivos .csv em stdout.
Ao contrário do awk
, perl
não rastreia a contagem de linhas de cada arquivo individual (ele rastreia apenas a contagem total de linhas, com a $.
variável). Este script mantém essa contagem manualmente, primeiro definindo a variável $fnr
no bloco BEGIN, depois incrementando-a para cada linha lida e, finalmente, redefinindo-a para 1 sempre que o final de um arquivo é atingido.
Isso é facilmente modificado para anexar o nome do arquivo como o último campo em vez do primeiro. por exemplo, altere as duas print
declarações para:
print "$_,NAME"
and:
print "$_,$fn"
Se você precisar inserir o campo nome do arquivo em algum outro lugar da linha, em vez de como o primeiro campo, você pode usar splice
a função do Perl.
Por exemplo, o seguinte insere o nome do arquivo como o terceiro campo (observe que os índices da matriz perl começam em zero, não em 1, portanto, o terceiro campo é $F[2]
, não $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
Isso usa a opção do Perl -F
para definir uma vírgula como delimitador de campo. Isso também permite que o recurso de divisão automática do Perl divida automaticamente a linha de entrada em um array chamado @F
(isto é semelhante ao comportamento padrão do awk de dividir automaticamente a linha de entrada em $1, $2, $3, etc). A string literal "NAME" ou o nome do arquivo modificado é dividido em @F e, em seguida, os elementos da @F
matriz são impressos, unidos por vírgulas.
Finalmente, se você quiser realmente alterar o conteúdo dos arquivos, use -i
a opção do Perl. Opcionalmente, você pode manter um backup do arquivo original usando uma "extensão" com a -i
opção, por exemplo, renomear filename.csv
para filename.csv.orig
with -iorig
. Por exemplo:
perl -iorig -lne '......' *.csv
ou
perl -iorig -F, -lne '......' *.csv
Responder4
Em seguida, faça um loop nos nomes dos arquivos e imprima as colunas com o 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
(...)