我觉得我应该能够在睡梦中做到这一点,但是假设我有两个文本文件,每个文件都有一列apache模块的名称,没有特定的顺序。一个文件有46个唯一(自身)字符串。另一个有67行和67个uniq(到文件)字符串。会有很多共同点。
我需要找到apache模块的名称,这些模块不是在较短的第一个文件中,而是在第二个较长的文件中。
我想通过搜索和比较字符串来做到这一点。行号,订单或邮件完全不相关。我只是想知道只需要在较长文件中列出哪些模块需要安装。
默认情况下,uniq,comm和diff想要按行和行号工作。 我不希望并排比较;我只想要一份清单。
答案 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