Зацикливание строк двух файлов параллельно [закрыто]

18

Цель сценария, который я создаю, - сравнить две серии файлов. Имена файлов сами хранятся в двух отдельных файлах, по одному пути на строку. Моя идея состоит в том, чтобы иметь два while readцикла, по одному для каждого списка имен файлов, но как я могу смешать два цикла вместе?

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

Мне нужно иметь возможность сравнивать файлы из двух разных списков одновременно через два цикла чтения ... Это вообще возможно?

mkrouse
источник
Вы собираетесь сделать этот сценарий королем испытаний? Если нет, то для сравнения файлов уже существуют мощные инструменты, например diff.
lgeorget
« Рода вызов», извините
lgeorget
@lgeorget ОП использует diff.
Тердон
ах, файлы из двух списков. Извините за бесполезные комментарии ...
lgeorget
Пожалуйста, избегайте перекрестных публикаций
iruvar

Ответы:

20

Вам не нужны две петли; вам просто нужно прочитать из двух файлов в одном цикле.

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
источник
Это намного больше кода, спасибо! Как обработать исключение пустой строки одновременно для двух циклов?
mkrouse
@mkrouse, я не знаю, что вы делали с этой переменной $ server там раньше, но как бы вы ни проверяли пустую строку для одной переменной, вы просто делаете то же самое для другой ...
psusi
7

Способ 1: используйте то, что вы знаете

Поскольку вы уже знаете, как перебрать один файл, вы можете объединить файлы и затем обработать объединенные файлы. Команда pasteобъединяет два файла построчно. Он помещает вкладку между строками из двух файлов, поэтому в этом решении предполагается, что в именах файлов нет вкладок. (Вы можете изменить разделитель, но вы должны найти символ, которого нет в имени файла.)

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

Если вы хотите пропустить пустые строки, вам нужно сделать это в каждом файле отдельно, так как pasteможет совпадать пустая строка из одного файла с непустой строкой из другого файла. Вы можете использовать grepдля фильтрации непустых строк.

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

Обратите внимание, что если два файла имеют разную длину, вы получите пустой $file2(независимо от того, какой список закончился первым).

Способ 2: зациклить два файла

Вы можете поместить любую сложную команду в состояние цикла while. Если вы поставите, read file1 <&3 && read file2 <&4цикл будет выполняться до тех пор, пока оба файла будут иметь строку для чтения, т.е. пока не закончится один файл.

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

Если вы хотите пропустить пустые строки, это немного сложнее, потому что вы должны пропустить два файла независимо друг от друга. Самый простой способ - разбить проблему на две части: пропустить пустые строки из одного файла и обработать непустые строки. Один из способов пропустить пустые строки - обработать, grepкак описано выше. Следите за тем, чтобы между <оператором перенаправления и тем <(, кто запускает команду, появилось свободное пространство.

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

Другой метод заключается в написании функции, которая ведет себя как, readно пропускает пустые строки. Эта функция может работать, вызывая readв цикле. Это не обязательно должна быть функция, но функция - лучший подход, как для организации вашего кода, так и потому, что этот фрагмент кода необходимо вызывать дважды. В функции ${!#}это экземпляр конструкции bash, ${!VARIABLE}которая оценивает значение переменной, имя которой равно значению VARIABLE; здесь переменная является специальной переменной, #которая содержит номер позиционного параметра, так же ${!#}как и последний позиционный параметр.

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
Жиль "ТАК - прекрати быть злым"
источник
Мне нравится использование -uопции чтения
Фелипе Альварес
1

Один подход будет использовать read -raвместо просто read. Если предположить , что filestoCompare.txtсодержащиеся 2 колонки с именами файлов в каждой, то read -raпрочитал бы обе колонки в то же время и назначить их в массив, compareFile. Затем к этому массиву можно получить доступ, чтобы индекс 0 был первым файлом, а индекс 1 - вторым файлом каждый раз в whileцикле.

пример

Скажем, у меня есть этот файл:, filestoCompare.txtи он содержит следующее:

file1 file2
file3 file4
file5 file6

Команда для просмотра этого файла будет выглядеть следующим образом:

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

Если 2 файла действительно являются отдельными файлами, такими как:

#list1
file1
file2
file3

#list2
file4
file5
file6

Их можно объединить с помощью pasteкоманды следующим образом:

$ paste list1 list2 > list1and2

Вот содержимое list1and2:

$ cat list1and2
file1   file4
file2   file5
file3   file6
SLM
источник
Но это не формат ввода: списки находятся в двух разных файлах. Вы могли бы joinих в первую очередь.
Жиль "ТАК - перестань быть злым"
@ Жиль - я знаю, что это не входной формат, я даже сказал, что "... Предполагая, что filestoCompare.txt содержал 2 столбца с именами файлов в каждом ...". Я понимаю ваше утверждение и не согласен. ФП не предоставил каких-либо дополнительных указаний по этому вопросу с момента его публикации.
SLM
@ Жиль - что если я добавлю пример, показывающий, как использовать команду pasteдля объединения двух файлов? Это заставит вас отказаться от голосования?
SLM