1

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

我需要找到-不在较短的第一个文件中但-在-在第二个较长的文件中的 apache 模块的名称。

我想通过搜索和比较字符串来做到这一点。行号、顺序或位置完全不相关。我只想知道需要安装哪些仅在较长文件中列出的模块。

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

4

2 回答 2

2

将您的字符串分成几行,对它们进行排序和唯一化,然后comm用于分析。(参见BashFAQ #36)。

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

文件1:

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

文件2:

...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 排序的-z标志可以处理 NUL 分隔符(以允许对包含换行符的字符串进行比较),comm但不能。但是,您可以在支持 NUL 分隔符的 shell 中编写自己的comm实现,例如以下示例:

#!/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
于 2012-02-21T02:34:08.210 回答
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

基本上,我只是设置了一个双循环,外循环上的文件较长,内循环上的文件较短。这样,较长列表中的每个项目都会与较短列表中的项目进行比较。这使我们能够在较小的列表中找到所有不匹配的项目。


编辑:我试图用这个更新的脚本来解决查尔斯的第一条评论:

#!/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
于 2012-02-21T02:48:42.460 回答