Bash shell 比较 2 个文件时的脚本检查
Bash shell scripting check while comparing 2 files
我有 2 个文件
文件 1
abc
cde
efg
hij
jkl
文件 2
abc
( * ) ( * ) ( * ) -- without the braces
efg
(*) hij -- without braces
(*) (*) lmn --- without braces
现在,逐行比较两个文件,即file1的第一行只与file2的第一行进行比较
abc ---- abc
cde ---- * * *
当遇到 * * * 时,比较应该移动到下一行比较其他行
然而,在比较时
hij --- (*) hij or jkl --- (*) (*) lmn
hij
必须与 File2 的 hij
进行比较并且必须给出 ok
并且,jkl
必须与 lmn
进行比较并且必须给出 not ok :忽略 *
或 * *
在任何一种情况下
我已经为相同的比较 2 个文件编写了脚本,但是我无法检查 *
。
你能帮我解决一下吗
比较文件的脚本片段
# 1. Read lines from file1 as string, and file2 as comma-separated array.
while read -r a && IFS=, read -ra b <&3; do
# 2. If both empty lines, continue.
if [[ "$a" == "" && ${#b[@]} == 0 ]]; then
continue
fi
# 3. Start assuming diff.
diff=1
# 4. Loop fields in $b.
for e in ${b[@]}; do
# Compare field in $b with $a, if match then abort.
if [[ "$e" == "$a" ]]; then
diff=0
break
fi
done
# 5. If no match found, print line from $b.
if [[ $diff == 1 ]]; then
# Join array with <space>comma.
line=$(printf ", %s" "${b[@]}")
# Print line, excluding leading <space>comma.
printf "%s\n" "${line:2}"
fi
# Input argument one as file 1 to stdin, and argument two as file 2 to
# file descriptor 3.
done < "" 3<""
您的脚本已经正确处理了单星号和双星号的情况。回想一下,您的脚本假定 diff=1,并且只有在找到匹配项时才更改为 diff=0。 ${b[@]}
中仅由一个星号组成的元素与来自 file1 的输入行比较不相等,这意味着这些元素正确地保留了原始假设 (diff=1) 不变。但是,如果来自 file1 的输入行只包含一个星号,则比较将导致匹配并设置 diff=0。但是 file2 中单个星号的含义会变得有些模棱两可;它是指 "match a literal single-asterisk line from file1",还是 "don't match any line from file1"?后一种感觉似乎是您希望 file2 中的星号表示的意思。如果您希望在这种奇怪的情况下保留这种感觉,则必须添加一个显式测试以跳过文件 2 中的星号词:
if [[ "$e" == '*' ]]; then continue; fi;
此测试将在 for
循环开始时进行。
关于三星号的情况,听起来您想完全跳过该情况。如上所述,当前来自 file2 的单星号元素被隐式跳过(因为它们不匹配来自 file1 的任何输入行),这使得 diff=1,并导致打印 * * *
消息。为了防止这种情况,您可以添加一个明确的保护措施来防止 * * *
,如下所示:
if [[ ${#b[@]} -eq 3 && "${b[0]}" == '*' && "${b[1]}" == '*' && "${b[2]}" == '*' ]]; then continue; fi;
此测试将在 while
循环开始附近进行,在空行检查之后。
我更喜欢下面的解决方案;您可以使用 bash 参数扩展的功能来忽略简化脚本的前导星号。
参见 bash 手册,"Parameter expansion"。
如果您不希望输出中出现星号,则必须将 if 语句中使用的参数扩展的结果分配给一个单独的变量,并在比较中使用它。
while read -r a && IFS=, read -ra b <&3
do
# If both lines are empty, continue.
if [ -z "$a" ] && [ -z "$b" ]
then
continue
fi
# If b contains three stars, don't compare.
if [ "$b" == "***" ]
then
continue
fi
# compare a and b ignoring leading *
if [ "$a" == "${b##\*}" ]
then
echo "$a: ok"
else
echo "$a/$b: nok"
fi
done < "" 3<""
此外,我会事先注意从文件中删除空行并检查两个输入文件的行数,以避免不匹配错误。
我有 2 个文件
文件 1
abc
cde
efg
hij
jkl
文件 2
abc
( * ) ( * ) ( * ) -- without the braces
efg
(*) hij -- without braces
(*) (*) lmn --- without braces
现在,逐行比较两个文件,即file1的第一行只与file2的第一行进行比较
abc ---- abc
cde ---- * * *
当遇到 * * * 时,比较应该移动到下一行比较其他行
然而,在比较时
hij --- (*) hij or jkl --- (*) (*) lmn
hij
必须与 File2 的 hij
进行比较并且必须给出 ok
并且,jkl
必须与 lmn
进行比较并且必须给出 not ok :忽略 *
或 * *
在任何一种情况下
我已经为相同的比较 2 个文件编写了脚本,但是我无法检查 *
。
你能帮我解决一下吗
比较文件的脚本片段
# 1. Read lines from file1 as string, and file2 as comma-separated array.
while read -r a && IFS=, read -ra b <&3; do
# 2. If both empty lines, continue.
if [[ "$a" == "" && ${#b[@]} == 0 ]]; then
continue
fi
# 3. Start assuming diff.
diff=1
# 4. Loop fields in $b.
for e in ${b[@]}; do
# Compare field in $b with $a, if match then abort.
if [[ "$e" == "$a" ]]; then
diff=0
break
fi
done
# 5. If no match found, print line from $b.
if [[ $diff == 1 ]]; then
# Join array with <space>comma.
line=$(printf ", %s" "${b[@]}")
# Print line, excluding leading <space>comma.
printf "%s\n" "${line:2}"
fi
# Input argument one as file 1 to stdin, and argument two as file 2 to
# file descriptor 3.
done < "" 3<""
您的脚本已经正确处理了单星号和双星号的情况。回想一下,您的脚本假定 diff=1,并且只有在找到匹配项时才更改为 diff=0。 ${b[@]}
中仅由一个星号组成的元素与来自 file1 的输入行比较不相等,这意味着这些元素正确地保留了原始假设 (diff=1) 不变。但是,如果来自 file1 的输入行只包含一个星号,则比较将导致匹配并设置 diff=0。但是 file2 中单个星号的含义会变得有些模棱两可;它是指 "match a literal single-asterisk line from file1",还是 "don't match any line from file1"?后一种感觉似乎是您希望 file2 中的星号表示的意思。如果您希望在这种奇怪的情况下保留这种感觉,则必须添加一个显式测试以跳过文件 2 中的星号词:
if [[ "$e" == '*' ]]; then continue; fi;
此测试将在 for
循环开始时进行。
关于三星号的情况,听起来您想完全跳过该情况。如上所述,当前来自 file2 的单星号元素被隐式跳过(因为它们不匹配来自 file1 的任何输入行),这使得 diff=1,并导致打印 * * *
消息。为了防止这种情况,您可以添加一个明确的保护措施来防止 * * *
,如下所示:
if [[ ${#b[@]} -eq 3 && "${b[0]}" == '*' && "${b[1]}" == '*' && "${b[2]}" == '*' ]]; then continue; fi;
此测试将在 while
循环开始附近进行,在空行检查之后。
我更喜欢下面的解决方案;您可以使用 bash 参数扩展的功能来忽略简化脚本的前导星号。
参见 bash 手册,"Parameter expansion"。
如果您不希望输出中出现星号,则必须将 if 语句中使用的参数扩展的结果分配给一个单独的变量,并在比较中使用它。
while read -r a && IFS=, read -ra b <&3
do
# If both lines are empty, continue.
if [ -z "$a" ] && [ -z "$b" ]
then
continue
fi
# If b contains three stars, don't compare.
if [ "$b" == "***" ]
then
continue
fi
# compare a and b ignoring leading *
if [ "$a" == "${b##\*}" ]
then
echo "$a: ok"
else
echo "$a/$b: nok"
fi
done < "" 3<""
此外,我会事先注意从文件中删除空行并检查两个输入文件的行数,以避免不匹配错误。