Bash 字符串上的差异,而不是行上的差异
我觉得我应该能够在睡眠中做到这一点,但假设我有两个文本文件,每个文件都有一列apache模块的名称,没有特定的顺序。一个文件有46个唯一(自身)字符串。另一个有67行和67个uniq(到文件)字符串。将有许多相同的字符串 我需要找到apache模块的名称,它们不在第一个较短的文件中,而是在第二个较长的文件中 我想通过搜索和比较字符串来实现这一点。行号、顺序或位置完全不相关。我只是想知道哪些只在较长的文件中列出的模块需要安装 默认情况下,uniq、comm和diff希望按行和行号工作。Bash 字符串上的差异,而不是行上的差异,bash,sorting,grep,diff,uniq,Bash,Sorting,Grep,Diff,Uniq,我觉得我应该能够在睡眠中做到这一点,但假设我有两个文本文件,每个文件都有一列apache模块的名称,没有特定的顺序。一个文件有46个唯一(自身)字符串。另一个有67行和67个uniq(到文件)字符串。将有许多相同的字符串 我需要找到apache模块的名称,它们不在第一个较短的文件中,而是在第二个较长的文件中 我想通过搜索和比较字符串来实现这一点。行号、顺序或位置完全不相关。我只是想知道哪些只在较长的文件中列出的模块需要安装 默认情况下,uniq、comm和diff希望按行和行号工作。 我不想要一
我不想要一个并列的比较;我只想要一份清单 将字符串分成几行,对它们进行排序和唯一化,并使用
comm
进行分析。(见附件)
举个例子,我假设您想要比较两个Apache配置文件之间的LoadModule
指令
文件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)
对于那些考虑到更有趣用例的人来说——不幸的是,虽然GNU sort的-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
#/bin/bash
exec3我会像这样运行一个小bash脚本(difference.bash):
这样运行:
$ ./differ.bash file1 file2
基本上,我只是设置了一个双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
#/bin/bash
f1=$1;#长文件
f2=2美元短文件
在读取项目时
做
其他=(“${others[@]}”“$item”)
已完成<$f2
在读取项目时
做
匹配=0
对于其他,以$others为单位
做
如果[“$item”==“$other”]
然后
匹配=1
打破
fi
完成
如果[$match!=1]
然后
echo$项目
fi
已完成<$f1
出口0
您能从所有涉及的文件中提取小样本吗?还有预期的输出?您是否关心是否有较短的项目没有出现在较长的文件中,或者这是不可能的?这两个文件都只是apache模块文件的列表,例如:mod_vhost_alias.so mod_mem_cache.so mod_status.so mod_ext_filter.so mod_authz_user.so mod_rewrite.so mod_imagemap.so mod_cgi。so@user189395如果是这样的话在这种情况下,您可以省去gawk位,直接通过sort-u
发送内容。如果文件是以空格分隔的,您可以在排序之前通过管道tr''\n'
将其更改为行分隔。使用barecat$f1
不仅会将文件内容公开为字符串拆分(在本例中,这可能是需要的),还会进行通配符扩展——例如,文件f*
中有一个条目,它将替换为当前目录中以f
开头的所有文件的名称。可能不是你想要的行为。此外,读取内部循环中的文件而不是事先将其内容存储在数组中一次是不必要的低效…除了具有高常量值的O(n*m)
复杂性之外,这种方法还要求两个列表能够同时放入内存中comm
一次只读取一行条目,GNU sort可以使用临时文件对大于可用RAM的输入进行排序和合并。同意,如果OP尝试比较每个1G+的文件,那么他可能会遇到一些问题。我不认为有必要解释这一点,因为他的档案包含46和67项。如果用于比较的文件变得非常大,他可能需要找到一种不同的方法。如果将它用作数组,我认为您需要在“${others[@]}”中为other指定。另外,您可以将数组追加为<代码> MyOrthAlx++=(“NeXiTIAL”)<代码>在非shell版本中……而且,由于您使用BASH,我将考虑<代码>((匹配!=1))< /代码>(匹配上下文)或<代码> [ [ $匹配!=1 ] ] < /代码>(字符串上下文,但作为shell关键字,在$match
上没有字符串拆分;否则,如果$match
为空,则可能会出现语法错误)。和echo“$item”
,而不是echo$item
,以避免变量内容的全局扩展。
$ ./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