Com base nos seus comentários acima e tendo observado que meus dados de teste são muito semelhantes aos seus dados reais, pude verificar se isso funciona:
grep -n '^ID.[^:-]*.[0-9][0-9]*$' |
sed -n 'h;s|\(.*\):6:\(ID.*\)||p;g;s||:|p'
sort -u |
sed 's|ID..*:||'
Eu grep
da pasta para as linhas que começam com ID
e o restante, e porque ela encontra vários arquivos correspondentes e pedi a linha correspondente -n
umbers grep
prints:
[filename]:[matching line number]:[IDmatch]
Eu passo isso para sed
, que salva a cópia da linha no buffer h
old, em seguida, verifica a string :6:ID
e, se encontrada, exclui tudo na linha até ID
. Então eu p
rint os resultados.
Depois disso, eu g
et recupero o buffer - sobrescrevendo minhas últimas edições no processo - e troco os locais na linha da correspondência de grep
e seu nome de arquivo correspondente. Assim, para cada linha grep
impressões de uma linha 6 correspondem sed
substitui por:
[IDmatch]
[IDmatch]:[filename]
Quando esses dados são passados para sort
, ele organiza todo o conjunto por ID
e, como só peço -u
nique resultados, ele exclui todos, exceto um, para linhas IDmatch
repetidas, mas retém as seguintes IDmatch:filename
linhas. A próxima instrução sed
apenas a limpa, tornando isso:
ID00000000
ID00000000:file00
ID00000000:file10
...
ID00000000:file80
ID00000001
ID00000001:file01
ID00000002
ID00000002:file02
...
Assim, ao invés disso:
ID00000000
file00
file10
...
file80
ID00000001
file01
ID00000002
file02
...
Mas essa solução será quebrada se um nome de arquivo contiver um caractere \n
ewline, embora o seguinte não seja. E eu trabalhei como colocar o seguinte em uma função de shell para que ele não precise ser copiado duas vezes - vou colá-lo aqui em breve.
for f in * ; do
sed '5!d;s|^|: "${'$((i=i+1))'}" |;q' "$f"
done |
sort -t' ' -k3 |
uniq -D -f2 |
sh -cx "$(cat)" -- * 2>&1
Isso deve ser feito - desde que você substitua a 5
na instrução sed
para quaisquer linhas em que seus ids estejam. Eu acho - e se eu estiver errado, deixe-me saber - isso lida com todos os casos de outra forma.
Para cada arquivo no diretório, ele incrementa um número por um e imprime uma linha que começa com a string ...
: "${[num]}" ...
... onde [num]
é um número inteiro real que acaba de ser incrementado em 1 e ...
é sua linha de identificação exclusiva.
Em seguida, ele canaliza essas linhas primeiro para sort
, que trata o caractere <space>
como um delimitador e classifica apenas os dados do terceiro campo em. O |pipeline
continua ao lado de uniq
, que também delimita <space>
e ignora os dois primeiros campos de entrada ao comparar sua entrada e imprimir apenas -D
linhas duplicadas. A próxima parte é um pouco estranha.
Então, em vez de ter que percorrer todo o caminho novamente e descobrir qual arquivo é qual, eu fiz o [num]
thing como mencionado. Quando o processo de sh
shell no final do |pipeline
é passado
resultados recebe apenas esses números. Mas já definiu seus parâmetros posicionais para o mesmo glob que estávamos interagindo enquanto incrementamos esses números - então, quando ele avalia esses números, ele os associará aos arquivos já em sua matriz posicional. Isso é tudo que faz.
Na verdade - quase nem faz isso. Cada parâmetro posicional é precedido pelo comando :
null. A única coisa que o processo do shell faz é avaliar as variáveis passadas para ele - ele nunca executa uma única linha de código. Mas eu o configurei para o modo de depuração -x
e redirecionei seu stderr
para stdout
para que ele imprima todos os nomes de arquivos.
Eu faço assim porque é muito mais fácil do que me preocupar com nomes estranhos de arquivos quebrando os resultados sort | uniq
. E isso funciona muito bem.
Eu testei isso com um conjunto de dados gerado da seguinte maneira:
tr -dc '[:graph:]' </dev/urandom |
dd ibs=100 cbs=10 conv=unblock count=91 |
split -b110 --filter='
{ c=${FILE##%%*0} ; c=${c#file}
sed "5cID000000${c:-00}"
} >$FILE' -ed - file ; rm *90*
Anote a string rm
acima. Eu estava ficando um pouco sonolenta e realmente não me importei em descobrir por que file89
estava sendo gerado com apenas 102bytes e não com 110bytes como o resto, então eu cheguei aos 90s e então rm
d. Executando o acima, os nomes dos arquivos serão combinados com o glob no diretório atual e sobrescreverão quaisquer arquivos de file00
- file89
, mas quando usados em um diretório de teste delegado é perfeitamente seguro.
... entre outros ... E funcionou para todos.
Que grava 90 arquivos denominados file[0-8][1-9]
, cada um com 1-4,6-10 linhas de 10 bytes de dados aleatórios e um ID exclusivo na linha 5 em cada arquivo. Também produz file[0-8]0
, em que as linhas 5 são sempre ID00000000
.
A saída da pequena função no topo executado neste conjunto de dados se parece com:
+ : file10 ID00000000
+ : file00 ID00000000
+ : file20 ID00000000
+ : file30 ID00000000
+ : file40 ID00000000
+ : file50 ID00000000
+ : file60 ID00000000
+ : file70 ID00000000
+ : file80 ID00000000
Se, por qualquer motivo, você não gostar dos símbolos +
na saída, apenas altere $PS4
para o último processo de shell. Você adiciona isso no início da última linha para lidar com isso:
PS4= sh ...
Mas você poderia, alternativamente, configurá-lo para qualquer string - ou até mesmo um bit executável de script de shell, se quiser, e ele irá separar os nomes dos arquivos como quiser. Basicamente, você pode usar o prompt como um delimitador automático. E esse último processo de shell ainda tem os nomes de arquivos em sua matriz - você pode adicionar comandos para manipular os dados de acordo com sua preferência.