Eu tenho um grande número de arquivos em uma pasta com um sistema de nomenclatura específico. Parece mais ou menos assim:
my_file_A_a.txt
my_file_A_d.txt
my_file_A_f.txt
my_file_A_t.txt
my_file_B_r.txt
my_file_B_x.txt
my_file_C_f.txt
my_file_D_f.txt
my_file_D_g.txt
my_file_E_r.txt
Eu gostaria de uma linha de comando ou uma série de comandos (pode usar arquivos temporários, tenho acesso de gravação), que retornaria algo como:
A: 4
B: 2
C: 1
D: 2
E: 1
Isso poderia ser feito com muitos ls -1 *A* | wc -l
comandos, mas levaria muito tempo, pois existem algumas centenas de "grupos" para contar.
Além disso, cada nome de grupo é exclusivo. Há um A
grupo, um B
grupo, mas nenhum AB
grupo.
Assumindo que seus nomes de arquivos são "bem comportados", ou seja, eles não contêm novas linhas, a seguinte combinação de
ls
eawk
funcionaria:Isso redirecionará a saída do
ls
comando que lista todos os arquivos que iniciammy_file*
em umawk
programa. Oawk
programa usará o_
separador de campo as e verificará o 3º campo para rastrear a ocorrência em um arraycount
, que usa o número do grupo como "índice do array".Ao final, imprime uma visão geral de quantas vezes cada grupo ocorreu.
Perceber
_
que não pode fazer parte da partea
,d
,f
,... dos nomes de arquivo em seu exemplo.awk
percorre os índices da matriz nofor (i in count)
loop. Se a classificação for desejada, você pode adicionar mais um pipe asort
. Alternativamente, se você usa GNU Awk, você pode adicionar uma configuração via antes daNF==4{...}
regra. Isso garantirá que os arrays sejam percorridos de acordo com o índice do array, classificados em ordem lexicográfica (ASCII).ls
.O
for
loop reformata cada nome de arquivof
para remover o inicialmy_file_
e o final_whatever.txt
, depois classifica essa saída e usauniq
para contar o número de ocorrências de cada valor exclusivo.Eu o abordaria com um loop sobre um curinga e, em seguida, extrairia o campo do nome do arquivo com o recurso de expressão regular do bash em sua
[[
construção de Expressão Condicional .O único campo entre parênteses é o terceiro delimitado por sublinhado; uma vez capturado, incrementamos esse valor em um array associativo (
collect
).Um nome de arquivo contendo quatro campos delimitados por sublinhado e terminando com a string
.txt
é correspondido pelo padrão de globbing estendido+([!_])_+([!_])_+([!_])_+([!_]).txt
. Cada+([!_])
um corresponde a um ou mais caracteres sem sublinhado, assim como[^_]+
faria com uma expressão regular estendida.Podemos extrair o terceiro campo removendo os dois campos iniciais e o último campo junto com a
.txt
string de sufixo.O script assume apenas que o terceiro campo no nome do arquivo não contém novas linhas incorporadas.
Testando isso nos nomes de arquivos de exemplo na pergunta:
Você pode filtrar isso por meio de um
awk
script simples para colocá-lo no formato que desejar.Se seus nomes forem bem comportados, o que significa que não há caracteres de nova linha incorporados em nenhum deles, você pode simplificar um pouco o script e usar
cut
em vez disso.Usando Raku (anteriormente conhecido como Perl_6)
Exemplo de entrada (lista de diretórios atual):
Saída de amostra:
Como uma breve explicação, a listagem de diretórios atual
dir()
é obtida e dividida em_
sublinhado. [Os nomes dos arquivos não começam/terminam com_
sublinhado]. Os elementos obtidos são assim:Depois disso, o Raku tem um mecanismo bastante robusto para gerar/compreender sequências: simplesmente digitar
[2,5,8...*]
permite extrair as letrasA,B,C,D,E
(a cada terceiro elemento, a numeração começa em0
). EntãoBag
,pairs
, esort
.(Se você tem certeza que não tem
espaços em branco em seus nomes de arquivos, você pode adicionar uma segunda chamada
split(" ")
após a primeira. Então os elementos que você retiraria seriam[2,6,10...*]
).NOTA 1: Se você tiver nomes de arquivos estranhos que não se encaixam no padrão listado pelo OP (e estão atrapalhando suas contagens), você pode alterar o
dir
chamada para algo comodir(test => / [ <-[_]>+ _ ] ** 3 /)
subconjuntos de nomes de arquivos em um regex onde um ou mais os não sublinhados são seguidos por um sublinhado, repetido três vezes.NOTA 2: Se você quiser duas colunas de saída (sem
=>
intermediários), basta alterar.say
para.put
. Ou se você preferir uma saída mais 'Raku-ish', tente usar.raku.say
, que retorna o seguinte:https://docs.raku.org/routine/dir
https://docs.raku.org/type/Bag
https://raku.org
Ordenar, sed e uniq o suficiente:
Outro oneliner, apenas 3 variáveis:
Precisa colocar uma linha extra na saída de classificação.