Faça um loop pelas linhas de dois arquivos em paralelo [fechado]

18

O objetivo do script que estou criando é comparar duas séries de arquivos. Os nomes dos arquivos são armazenados em dois arquivos separados, um caminho por linha. Minha idéia é ter dois while readloops, um para cada lista de nomes de arquivos, mas como posso misturar os dois loops?

while read compareFile <&3; do     
 if [[ ! $server =~ [^[:space:]] ]] ; then  #empty line exception
    continue
 fi   
    echo "Comparing file - $compareFile"
 if diff "$compareFile" _(other file from loop?_) >/dev/null ; then
     echo Same
 else
      echo Different
 fi 
done 3</infanass/dev/admin/filestoCompare.txt

Eu preciso ser capaz de comparar arquivos de duas listas diferentes ao mesmo tempo através de dois enquanto lê loops ... Isso é possível?

mkrouse
fonte
Você pretende fazer esse roteiro como um rei do desafio? Caso contrário, já existem ferramentas poderosas para comparar arquivos diff.
lgeorget
" tipo de desafio", desculpe
lgeorget 10/07/2013
@lgeorget que o OP está usando diff.
terdon
ah, arquivos de duas listas. Desculpe pelos comentários inúteis ...
lgeorget
Evite postagem cruzada
iruvar

Respostas:

20

Você não precisa de dois loops; você só precisa ler de dois arquivos em um loop.

while read compareFile1 <&3 && read compareFile2 <&4; do     
 if [[ ! $server =~ [^[:space:]] ]] ; then  #empty line exception
    continue
 fi   
    echo "Comparing file - $compareFile"
 if diff "$compareFile1" "$compareFile2" >/dev/null ; then
     echo Same
 else
      echo Different
 fi 
done 3</infanass/dev/admin/filestoCompare.txt 4<other_file
psusi
fonte
isso é muito menos código, obrigado! como lidar com uma exceção de linha vazia ao mesmo tempo para os dois loops?
Mcrouse
@mkrouse, eu não sei o que você estava fazendo com aquela variável $ server lá antes, mas no entanto você testar uma linha vazia por um variável, você acabou de fazer o mesmo no outro ...
psusi
7

Método 1: use o que você sabe

Como você já sabe fazer um loop sobre um arquivo, você pode combinar os arquivos e depois processá-los. O comando pasteune dois arquivos linha por linha. Ele coloca uma guia entre as linhas provenientes dos dois arquivos, portanto, esta solução assume que não há guias nos nomes dos arquivos. (Você pode alterar o separador, mas precisa encontrar um caractere que não esteja presente no nome de um arquivo.)

paste -- "$list1.txt" "list2.txt" |
while IFS=$'\t' read -r file1 file2 rest; do
  diff -q -- "$file1" "$file2"
  case $? in
    0) status='same';;
    1) status='different';;
    *) status='ERROR';;
  esac
  echo "$status $file1 $file2"
done

Se você quiser pular linhas em branco, precisará fazê-lo em cada arquivo separadamente, pois pastepode corresponder uma linha em branco de um arquivo com uma linha não em branco de outro arquivo. Você pode usar greppara filtrar as linhas que não estão em branco.

paste -- <(grep '[^[:space:]]' "$list1.txt") <(grep '[^[:space:]]' "list2.txt") |
while IFS=$'\t' read -r file1 file2 rest; do
  

Observe que, se os dois arquivos tiverem comprimentos diferentes, você ficará vazio $file2(independentemente da lista que terminou primeiro).

Método 2: loop sobre dois arquivos

Você pode colocar um comando tão complexo quanto desejar na condição do loop while. Se você colocar read file1 <&3 && read file2 <&4, o loop será executado enquanto os dois arquivos tiverem uma linha para ler, ou seja, até que um arquivo se esgote.

while read -u 3 -r file1 && read -u 4 -r file2; do
  
done 3<list1..txt 4<list2.txt

Se você quiser pular linhas em branco, é um pouco mais complicado, porque é necessário pular os dois arquivos de forma independente. A maneira mais fácil é dividir o problema em duas partes: pule as linhas em branco de um arquivo e processe as linhas que não estiverem em branco. Um método para ignorar as linhas em branco é processar grepcomo acima. Cuidado com o espaço necessário entre o <operador de redirecionamento e o <(que inicia uma suspeita de comando.

while read -u 3 -r file1 && read -u 4 -r file2; do
  
done 3< <(grep '[^[:space:]]' "$list1.txt") 4< <(grep '[^[:space:]]' "list2.txt")

Outro método é escrever uma função que se comporte como, readmas pule linhas em branco. Esta função pode funcionar chamando readum loop. Não precisa ser uma função, mas uma função é a melhor abordagem, para organizar seu código e porque esse trecho de código precisa ser chamado duas vezes. Na função, ${!#}é uma instância do construto bash ${!VARIABLE}que avalia o valor da variável cujo nome é o valor de VARIABLE; aqui a variável é a variável especial #que contém o número de parâmetro posicional, assim ${!#}como o último parâmetro posicional.

function read_nonblank {
  while read "$@" &&
        [[ ${!#} !~ [^[:space:]] ]]
  do :; done
}
while read_nonblank -u 3 -r file1 && read_nonblank -u 4 -r file2; do
  
done 3<list1..txt 4<list2.txt
Gilles 'SO- parar de ser mau'
fonte
Eu gosto do uso da -uopção de leitura
Felipe Alvarez
1

Uma abordagem seria usar em read -ravez de apenas read. Assumindo as filestoCompare.txt2 colunas contidas com os nomes dos arquivos em cada uma, read -raelas leriam as duas colunas ao mesmo tempo e as atribuiriam a uma matriz compareFile,. Essa matriz pode ser acessada para que o índice 0 seja o primeiro arquivo e o índice 1 seja o 2º arquivo a cada vez no whileloop.

Exemplo

Digamos que eu tenho este arquivo: filestoCompare.txte ele contém o seguinte:

file1 file2
file3 file4
file5 file6

O comando para passar por esse arquivo seria o seguinte:

$ while read -ra a ; do printf "%s\t%s\n" ${a[0]} ${a[1]}; done < filestoCompare.txt
file1   file2
file3   file4
file5   file6

Se os 2 arquivos são realmente arquivos separados, como:

#list1
file1
file2
file3

#list2
file4
file5
file6

Eles podem ser unidos ao pastecomando da seguinte maneira:

$ paste list1 list2 > list1and2

Aqui está o conteúdo de list1and2:

$ cat list1and2
file1   file4
file2   file5
file3   file6
slm
fonte
Mas esse não é o formato de entrada: as listas estão em dois arquivos diferentes. Você poderia joineles primeiro.
Gilles 'SO- stop be evil' ''
@ Gilles - Eu sei que esse não é o formato de entrada, acredito que até disse que "... Supondo que o filestoCompare.txt contivesse 2 colunas com os nomes dos arquivos em cada ...". Entendo sua afirmação e não discordo. O OP não forneceu mais orientações sobre essa questão desde a publicação.
Slm
@ Gilles - e se eu adicionar um exemplo mostrando como usar o comando pastepara ingressar nos 2 arquivos? Isso levaria você a não votar?
Slm