字符串上的差异,而不是行

时间:2012-02-21 02:24:43

标签: bash sorting grep diff uniq

我觉得我应该能够在睡梦中做到这一点,但是假设我有两个文本文件,每个文件都有一列apache模块的名称,没有特定的顺序。一个文件有46个唯一(自身)字符串。另一个有67行和67个uniq(到文件)字符串。会有很多共同点。

我需要找到apache模块的名称,这些模块不是在较短的第一个文件中,而是在第二个较长的文件中。

我想通过搜索和比较字符串来做到这一点。行号,订单或邮件完全不相关。我只是想知道只需要在较长文件中列出哪些模块需要安装。

默认情况下,uniq,comm和diff想要按行和行号工作。 我不希望并排比较;我只想要一份清单。

2 个答案:

答案 0 :(得分:2)

将字符串分解为行,对它们进行排序和统一,并使用comm进行分析。 (见BashFAQ #36)。

我想假设您想要比较两个Apache配置文件之间的LoadModule指令。

文件1:

...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

对于那些用更有趣的用例来看这个问题的人 - 不幸的是,虽然GNU sort的-z标志可以处理NUL分隔符(为了允许对包含换行符的字符串进行比较),comm不能。但是,您可以在shell中编写自己的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 :(得分:1)

我会像这样运行一个小的bash脚本(differ.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

基本上,我只是设置一个double for循环,外循环上的文件较长,内循环上的文件较短。这样,较长列表中的每个项目都与较短列表中的项目进行比较。这允许我们找到与较小列表中的内容不匹配的所有项目。


修改:我试图用这个更新的脚本来解决Charles的第一条评论:

#!/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