因此,基本上我想做的是按列2逐行比较两个文件。我该怎么做?
File_1.txt:
User1 US
User2 US
User3 US
File_2.txt:
User1 US
User2 US
User3 NG
输出文件:
User3 has changed
因此,基本上我想做的是按列2逐行比较两个文件。我该怎么做?
File_1.txt:
User1 US
User2 US
User3 US
File_2.txt:
User1 US
User2 US
User3 NG
输出文件:
User3 has changed
Answers:
查看diff
命令。这是一个很好的工具,您可以通过man diff
在终端中键入内容来阅读所有内容 。
您要执行的命令diff File_1.txt File_2.txt
将输出两者之间的差异,并且应如下所示:
关于读取第三条命令的输出的简要说明:“箭头”(<
和>
)表示左文件(<
)与右文件(>
)中行的值,其中左文件是您输入的首先在命令行上,在这种情况下File_1.txt
另外,您可能会注意到第4条命令是diff ... | tee Output_File
将结果从管道传送diff
到中tee
,然后将其输出到文件中,以便您不想稍后在控制台上查看全部内容时将其保存以供以后使用。
diff file1 file2 -s
。这是一个示例:imgur.com/ShrQx9x
cmp
比diff
返回码要快得多。
下面的脚本可以正确解决问题(file1,file2,带有“已更改”消息的输出文件)。
将脚本复制到一个空文件中,另存为compare.py
,使其可执行,然后通过以下命令运行该脚本:
/path/to/compare.py <file1> <file2> <outputfile>
剧本:
#!/usr/bin/env python
import sys
file1 = sys.argv[1]; file2 = sys.argv[2]; outfile = sys.argv[3]
def readfile(file):
with open(file) as compare:
return [item.replace("\n", "").split(" ") for item in compare.readlines()]
data1 = readfile(file1); data2 = readfile(file2)
mismatch = [item[0] for item in data1 if not item in data2]
with open(outfile, "wt") as out:
for line in mismatch:
out.write(line+" has changed"+"\n")
额外增加几行,您可以使其输出到输出文件或终端,具体取决于是否定义了输出文件:
要打印到文件:
/path/to/compare.py <file1> <file2> <outputfile>
要打印到终端窗口:
/path/to/compare.py <file1> <file2>
剧本:
#!/usr/bin/env python
import sys
file1 = sys.argv[1]; file2 = sys.argv[2]
try:
outfile = sys.argv[3]
except IndexError:
outfile = None
def readfile(file):
with open(file) as compare:
return [item.replace("\n", "").split(" ") for item in compare.readlines()]
data1 = readfile(file1); data2 = readfile(file2)
mismatch = [item[0] for item in data1 if not item in data2]
if outfile != None:
with open(outfile, "wt") as out:
for line in mismatch:
out.write(line+" has changed"+"\n")
else:
for line in mismatch:
print line+" has changed"
一种简单的方法是使用colordiff
,其行为类似于diff
但会使其输出变色。这对于阅读差异非常有帮助。用你的例子,
$ colordiff -u File_1.txt File_2.txt
--- File_1.txt 2016-12-24 17:59:17.409490554 -0500
+++ File_2.txt 2016-12-24 18:00:06.666719659 -0500
@@ -1,3 +1,3 @@
User1 US
User2 US
-User3 US
+User3 NG
该u
选项提供统一的差异。这就是彩色差异的样子:
colordiff
通过运行安装sudo apt-get install colordiff
。
如果不需要知道文件的哪些部分不同,则可以使用文件的校验和。使用md5sum
或有很多方法可以做到这一点sha256sum
。基本上,它们每个都输出一个字符串,文件内容将散列到该字符串。如果两个文件相同,则它们的哈希也将相同。当您下载软件(例如Ubuntu安装iso映像)时,通常会使用它。它们通常用于验证下载内容的完整性。
考虑下面的脚本,您可以在其中提供两个文件作为参数,该文件将告诉您它们是否相同。
#!/bin/bash
# Check if both files exist
if ! [ -e "$1" ];
then
printf "%s doesn't exist\n" "$1"
exit 2
elif ! [ -e "$2" ]
then
printf "%s doesn't exist\n" "$2"
exit 2
fi
# Get checksums of eithe file
file1_sha=$( sha256sum "$1" | awk '{print $1}')
file2_sha=$( sha256sum "$2" | awk '{print $1}')
# Compare the checksums
if [ "x$file1_sha" = "x$file2_sha" ]
then
printf "Files %s and %s are the same\n" "$1" "$2"
exit 0
else
printf "Files %s and %s are different\n" "$1" "$2"
exit 1
fi
样品运行:
$ ./compare_files.sh /etc/passwd ./passwd_copy.txt
Files /etc/passwd and ./passwd_copy.txt are the same
$ echo $?
0
$ ./compare_files.sh /etc/passwd /etc/default/grub
Files /etc/passwd and /etc/default/grub are different
$ echo $?
1
此外,还有一个comm
命令,该命令比较两个排序的文件,并在3列中提供输出:第1列表示文件#1唯一的项目,第2列表示文件#2唯一的项目,第3列表示两个文件中都存在的项目。
要禁止显示任一列,可以使用开关-1,-2和-3。使用-3将显示不同的行。
在下面,您可以看到正在使用的命令的屏幕截图。
仅有一项要求-必须对文件进行排序,以便正确比较它们。sort
命令可以用于此目的。贝娄(Bellow)是另一个屏幕截图,其中对文件进行排序然后进行比较。仅从左贝隆开始到File_1的行,从第二列开始的行仅属于File_2
安装git并使用
$ git diff filename1 filename2
这样您将获得漂亮的彩色格式输出
Git安装
$ apt-get update
$ apt-get install git-core
比较格式为的2个文件中的名称/值对name value\n
。写入name
到Output_file
是否改变。需要bash v4 +用于关联数组。
$ ./colcmp.sh File_1.txt File_2.txt
User3 changed from 'US' to 'NG'
no change: User1,User2
$ cat Output_File
User3 has changed
cmp -s "$1" "$2"
case "$?" in
0)
echo "" > Output_File
echo "files are identical"
;;
1)
echo "" > Output_File
cp "$1" ~/.colcmp.array1.tmp.sh
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array1.tmp.sh
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.array1.tmp.sh
chmod 755 ~/.colcmp.array1.tmp.sh
declare -A A1
source ~/.colcmp.array1.tmp.sh
cp "$2" ~/.colcmp.array2.tmp.sh
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array2.tmp.sh
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array2.tmp.sh
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A2\\[\\1\\]=\"\\2\"/" ~/.colcmp.array2.tmp.sh
chmod 755 ~/.colcmp.array2.tmp.sh
declare -A A2
source ~/.colcmp.array2.tmp.sh
USERSWHODIDNOTCHANGE=
for i in "${!A1[@]}"; do
if [ "${A2[$i]+x}" = "" ]; then
echo "$i was removed"
echo "$i has changed" > Output_File
fi
done
for i in "${!A2[@]}"; do
if [ "${A1[$i]+x}" = "" ]; then
echo "$i was added as '${A2[$i]}'"
echo "$i has changed" > Output_File
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
echo "$i changed from '${A1[$i]}' to '${A2[$i]}'"
echo "$i has changed" > Output_File
else
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
fi
done
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
;;
*)
echo "error: file not found, access denied, etc..."
echo "usage: ./colcmp.sh File_1.txt File_2.txt"
;;
esac
据我所知,代码的分解及其含义。我欢迎您提出修改和建议。
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
我选择使用case .. esac语句评估$?因为$的价值?每个命令(包括测试([))之后的变化。
cmp -s "$1" "$2"
CMPRESULT=$?
if [ $CMPRESULT -eq 0 ]; then
# match
elif [ $CMPRESULT -eq 1 ]; then
# compare
else
# error
fi
以上与case语句具有相同的作用。我更喜欢IDK。
echo "" > Output_File
上面清除了输出文件,因此,如果没有用户更改,则输出文件将为空。
我在case语句中执行此操作,以便Output_file在出错时保持不变。
cp "$1" ~/.colcmp.arrays.tmp.sh
上面将File_1.txt复制到当前用户的主目录。
例如,如果当前用户是john,则上述内容将与cp“ File_1.txt” /home/john/.colcmp.arrays.tmp.sh相同
基本上,我很偏执。我知道这些字符在脚本中作为变量赋值的一部分运行时可能具有特殊含义或执行外部程序:
我不知道我对bash有多少了解。我不知道其他哪些字符可能有特殊含义,但是我想用反斜杠将它们全部转义:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed可以比正则表达式模式匹配做更多的事情。脚本模式 “ s /(查找)/(替换)/”专门执行模式匹配。
“ s /(查找)/(替换)/(修饰符)”
英文:捕获所有标点符号或特殊字符作为捕获组1(\\ 1)
英文:在所有特殊字符前加反斜杠
用英语:如果在同一行上找到多个匹配项,请全部替换
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh
上面使用正则表达式在〜/ .colcmp.arrays.tmp.sh的每一行前添加bash注释字符(#)。之所以这样做,是因为以后我打算使用source命令执行〜/ .colcmp.arrays.tmp.sh,并且因为我不确定File_1.txt的整体格式。
我不想意外地执行任意代码。我认为没有人这样做。
“ s /(查找)/(替换)/”
用英语:捕获每一行作为捕获组1(\\ 1)
用英语:用磅符号替换每行,后跟替换的行
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
以上是该脚本的核心。
#User1 US
A1[User1]="US"
A2[User1]="US"
对于第二个文件)“ s /(查找)/(替换)/”
用英语:
捕获其余的行作为捕获组2
(替换)= A1 \\ [\\ 1 \\] = \“ \\ 2 \”
用英语:用格式#name value
的数组赋值运算符替换格式中的每一行A1[name]="value"
chmod 755 ~/.colcmp.arrays.tmp.sh
上面使用chmod使阵列脚本文件可执行。
我不确定这是否有必要。
declare -A A1
大写字母-A表示声明的变量将是关联数组。
这就是为什么脚本需要bash v4或更高版本的原因。
source ~/.colcmp.arrays.tmp.sh
我们已经:
User value
为的行A1[User]="value"
,上面我们源脚本在当前shell中运行它。我们这样做是为了保留脚本设置的变量值。如果直接执行该脚本,它将生成一个新的shell,并且当新的shell退出时变量值会丢失,或者至少是我的理解。
cp "$2" ~/.colcmp.array2.tmp.sh
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array2.tmp.sh
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array2.tmp.sh
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A2\\[\\1\\]=\"\\2\"/" ~/.colcmp.array2.tmp.sh
chmod 755 ~/.colcmp.array2.tmp.sh
declare -A A2
source ~/.colcmp.array2.tmp.sh
我们为$ 1和A1做的事情与为$ 2和A2做的事情一样。它确实应该是一个功能。我认为这时该脚本已经很混乱了,并且可以正常工作,所以我不会解决它。
for i in "${!A1[@]}"; do
# check for users removed
done
上面循环通过关联数组键
if [ "${A2[$i]+x}" = "" ]; then
上面使用变量替换来检测未设置的值与已显式设置为零长度字符串的变量之间的差异。
显然,有很多方法可以查看是否已设置变量。我选择了得票最多的那个。
echo "$i has changed" > Output_File
上面将用户$ i添加到Output_File
USERSWHODIDNOTCHANGE=
上面清除了一个变量,因此我们可以跟踪未更改的用户。
for i in "${!A2[@]}"; do
# detect users added, changed and not changed
done
上面循环通过关联数组键
if ! [ "${A1[$i]+x}" != "" ]; then
上面使用变量替换来查看是否已设置变量。
echo "$i was added as '${A2[$i]}'"
因为$ i是数组键(用户名),所以$ A2 [$ i]应该从File_2.txt返回与当前用户关联的值。
例如,如果$ i为User1,则上面的内容为$ {A2 [User1]}
echo "$i has changed" > Output_File
上面将用户$ i添加到Output_File
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
因为$ i是数组键(用户名),所以$ A1 [$ i]应该从File_1.txt返回与当前用户关联的值,而$ A2 [$ i]应该从File_2.txt返回该值。
上面比较了两个文件中用户$ i的关联值。
echo "$i has changed" > Output_File
上面将用户$ i添加到Output_File
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
上面创建了一个逗号分隔的未更改用户列表。请注意,列表中没有空格,否则将需要引用下一个检查。
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
上述报告的价值$ USERSWHODIDNOTCHANGE但前提是在一个价值$ USERSWHODIDNOTCHANGE。编写方式中,$ USERSWHODIDNOTCHANGE不能包含任何空格。如果确实需要空格,可以将上面的内容重写如下:
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
diff "File_1.txt" "File_2.txt"