Различаются по строкам, а не по строкам - PullRequest
1 голос
/ 21 февраля 2012

Мне кажется, что я смогу сделать это во сне, но, скажем, у меня есть два текстовых файла, в каждом из которых есть один столбец с именами модулей apache в произвольном порядке.Один файл имеет 46 уникальных (для себя) строк.Другой имеет 67 строк и 67 уникальных (в файл) строк.Там будет много общих строк.

Мне нужно найти имена модулей apache, которые находятся не в первом, более коротком файле, а во втором, более длинном файле.

Я хочу сделать это путем поиска и сравнения строк .Номер строки, заказ или позиция совершенно не имеют значения.Я просто хочу знать, какие модули, перечисленные только в более длинном файле, должны быть установлены.

По умолчанию uniq, comm и diff хотят работать по строкам и номерам строк.Я не хочу параллельного сравнения;Я просто хочу список.

Ответы [ 2 ]

2 голосов
/ 21 февраля 2012

Разбейте строки на строки, отсортируйте и удалите их и используйте comm для анализа.(См. BashFAQ # 36 ).

Предположим, на примере, что вы хотите сравнить директивы LoadModule между двумя файлами конфигурации Apache.

file1:

...other stuff...
LoadModule foo modules/foo.so
LoadModule bar modules/bar.so
LoadModule baz modules/baz.so
...other stuff...

file2:

...other stuff...
LoadModule foo modules/foo.so
...other stuff...

Итак, для этого:

comm -2 -3 \
  <(gawk '/LoadModule/ { print $2 }' file1 | sort -u)
  <(gawk '/LoadModule/ { print $2 }' file2 | sort -u)

... будет подавлять любые строки, найденные в обоихили только в более коротком файле, и дать вам имена модулей, найденные в третьем, что дает следующий вывод:

bar
baz

Для тех, кто рассматривает этот вопрос, имея в виду более интересные варианты использования - к сожалению, покаФлаг -z сортировки GNU может обрабатывать разделители NUL (для сравнения строк, содержащих новые строки), comm не может.Однако вы можете написать свою собственную реализацию comm в оболочке, которая поддерживает разделители NUL, например, в следующем примере:

#!/bin/bash
exec 3<"$1" 4<"$2"

IFS='' read -u 4 -d ''; input_two="$REPLY"

while IFS='' read -u 3 -d '' ; do
    input_one="$REPLY"
    while [[ $input_two < $input_one ]] ; do
        IFS='' read -u 4 -d '' || exit 0
        input_two="$REPLY"
    done
    if [[ $input_two = "$input_one" ]] ; then
        printf '%s\0' "$input_two"
    fi
done
1 голос
/ 21 февраля 2012

Я бы запустил маленький скрипт bash, такой как (dif.bash):

#!/bin/bash
f1=$1; # longer file
f2=$2; # shorter file

for item in `cat $f1`
do
    match=0
    for other in `cat $f2`
    do
        if [ "$item" == "$other" ]
        then
            match=1
            break
        fi
    done
    if [ $match != 1 ]
    then
        echo $item
    fi
done

exit 0

Запустите его так:

$ ./differ.bash file1 file2

По сути, я просто настраиваю двойнойцикл for с более длинным файлом во внешнем цикле и более коротким файлом во внутреннем цикле.Таким образом, каждый элемент в более длинном списке сравнивается с элементами в более коротком списке.Это позволяет нам находить все элементы, которые не соответствуют чему-либо, в меньшем списке.

Редактировать: Я попытался ответить на первый комментарий Чарльза с помощью этого обновленного сценария:

#!/bin/bash
f1=$1; # longer file
f2=$2; # shorter file

while read item
do
    others=( "${others[@]}" "$item" )
done < $f2

while read item
do
    match=0
    for other in $others
    do
        if [ "$item" == "$other" ]
        then
            match=1
            break
        fi
    done
    if [ $match != 1 ]
    then
        echo $item
    fi
done < $f1

exit 0
...