比较 linux KSH 脚本中的两个字符串
Comparing two strings in linux KSH script
我有两个文件:file1 和 file2。以下是文件内容示例:
<TG>
<entry name="KEYNAME" val="" type="string" />
<entry name="KEYTYPE" val="" type="string" />
<entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
...
</TG>
我想检查file1中的某些行(包含字符串"entry name"的行)是否存在于file2中,如果存在,则比较这两行是否相同。
我实际上是通过复制 file1 创建了 file2,然后更改了一些值。问题是比较两个字符串变量并没有 return 正确的值。虽然在显示这两个变量时我可以看到它们是相同的,但比较的结果表明它们不是。我正在使用 Ksh。这是我的代码:
while read p; do
if [[ $p == *"entry name"* ]]; then
PARAM_NAME=$(echo $p | cut -d '"' -f2)
echo $PARAM_NAME
PARAM_OLD=$(grep $PARAM_NAME file2)
if [[ $PARAM_OLD == *"entry name"* ]]; then
echo $PARAM_OLD
echo $p
if [ "$PARAM_OLD" = "$p" ]; then
echo 'Identical values'
else
echo 'Different values'
fi
else
echo "$PARAM_NAME does not exist in previous version file. Using default value"
fi
fi
done <file1
我尝试了括号、等号和引号的所有可能性([]、[[]]、=、==、""、'""' 等)
这是我得到的输出:
<entry name="KEYNAME" val="" type="string" />
KEYNAME
<entry name="KEYNAME" val="" type="string" />
<entry name="KEYNAME" val="" type="string" />
Different values
<entry name="KEYTYPE" val="" type="string" />
KEYTYPE
<entry name="KEYTYPE" val="" type="string" />
<entry name="KEYTYPE" val="" type="string" />
Different values
<entry name="TIMEZONE_OFFSET" val="-24" type="INT16" />
TIMEZONE_OFFSET
<entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
<entry name="TIMEZONE_OFFSET" val="-24" type="INT16" />
Different values
我仍然发现字符串不同!我将不胜感激任何解释和帮助。
您的 if 语句中只有 1 个 =,请将其更改为两个:
if [ "$PARAM_OLD" = "$p" ]; then
至:
if [ "$PARAM_OLD" == "$p" ]; then
此外(现在不是问题,但可能是你的下一个问题),在下面的行中用 " 将 $PARAM_OLD 括起来:
if [[ $PARAM_OLD == *"entry name"* ]]; then
所以它变成了:
if [[ "$PARAM_OLD" == *"entry name"* ]]; then
大多数 recent/newer 操作系统都支持 ksh 和 ksh93。
对于 ksh93,我们可以使用关联数组来限制我们对每个文件进行一次传递。
首先是一些示例数据:
$ cat file1
<TG>
<entry name="KEYNAME" val="" type="string" />
<entry name="KEYTYPE" val="" type="string" />
<entry name="KEYATTRIB" val="" type="string" />
<entry name="TIMEZONE_OFFSET" val="-241" type="INT16" />
</TG>
$ cat file2
<TG>
<entry name="KEYNAME" val="" type="string" />
<entry name="KEYTYPE" val="" type="stringX" />
<entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
</TG>
ksh93 脚本:
$ cat my_comp
#!/bin/ksh93
unset pline
typeset -A pline
# pull unique list of 'entry name' lines from file2 and store in our associative array pline[]:
egrep "entry name" file2 | sort -u | while read line
do
# strip out the 'entry name' value
x=${line#*\"}
pname=${x%%\"*}
# use the 'entry name' value as the index for our pline[] array
pline[${pname}]=${line}
done
# for each unique 'entry name' line in file1, see if we have a match in file2 (aka our pline[] array):
egrep "entry name" file1 | sort -u | while read line
do
# again, strip out the 'entry name' value
x=${line#*\"}
pname=${x%%\"*}
# if pname does not exist in file2
[ "${pline[${pname}]}" = '' ] && \
echo "\npname = '${pname}' : Does not exist in file2. Using default value:" && \
echo "file1: ${line}" && \
continue
# if pname exists in file2 but line is different
[ "${pline[${pname}]}" = "${line}" ] && \
echo "\npname = '${pname}' : Identical values for pname" && \
echo "file1: ${line}" && \
echo "file2: ${pline[${pname}]}" && \
continue
# if pname exists in file2 and line is the same
[ "${pline[${pname}]}" != "${line}" ] && \
echo "\npname = '${pname}' : Different values for pname" && \
echo "file1: ${line}" && \
echo "file2: ${pline[${pname}]}"
done
运行 针对示例文件的脚本:
$ my_comp
pname = 'KEYATTRIB' : Does not exist in file2. Using default value:
file1: <entry name="KEYATTRIB" val="" type="string" />
pname = 'KEYNAME' : Identical values for pname
file1: <entry name="KEYNAME" val="" type="string" />
file2: <entry name="KEYNAME" val="" type="string" />
pname = 'KEYTYPE' : Different values for pname
file1: <entry name="KEYTYPE" val="" type="string" />
file2: <entry name="KEYTYPE" val="" type="stringX" />
pname = 'TIMEZONE_OFFSET' : Different values for pname
file1: <entry name="TIMEZONE_OFFSET" val="-241" type="INT16" />
file2: <entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
回到普通的基本 ksh:
$ cat my_comp2
#!/bin/ksh
egrep "entry name" file1 | sort -u | while read line1
do
x=${line1#*\"}
pname=${x%%\"*}
# see if we can find a matching line in file2; need to strip off leading
# spaces in order to match with line1
unset line2
line2=$( egrep "entry name.*${pname}" file2 | sed 's/^ *//g' )
# if pname does not exist in file2
[ "${line2}" = '' ] && \
echo "\npname = '${pname}' : Does not exist in file2. Using default value:" && \
echo "file1: ${line1}" && \
continue
# if pname exists in file2 but lines are different
[ "${line2}" = "${line1}" ] && \
echo "\npname = '${pname}' : Identical values for pname" && \
echo "file1: ${line1}" && \
echo "file2: ${line2}" && \
continue
# if pname exists in file2 and lines are the same
[ "${line2}" != "${line1}" ] && \
echo "\npname = '${pname}' : Different values for pname" && \
echo "file1: ${line1}" && \
echo "file2: ${line2}"
done
运行 针对示例文件的脚本:
$ my_comp2
pname = 'KEYATTRIB' : Does not exist in file2. Using default value:
file1: <entry name="KEYATTRIB" val="" type="string" />
pname = 'KEYNAME' : Identical values for pname
file1: <entry name="KEYNAME" val="" type="string" />
file2: <entry name="KEYNAME" val="" type="string" />
pname = 'KEYTYPE' : Different values for pname
file1: <entry name="KEYTYPE" val="" type="string" />
file2: <entry name="KEYTYPE" val="" type="stringX" />
pname = 'TIMEZONE_OFFSET' : Different values for pname
file1: <entry name="TIMEZONE_OFFSET" val="-241" type="INT16" />
file2: <entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
从 XML 转换为制表符分隔的 Key/Value 对
以下内容会将您的内容转换为制表符分隔的键值形式:
xml_to_tsv() {
xmlstarlet sel -t -m '//entry[@name]' -v ./@name -o $'\t' -v ./@value -n
}
从每行中提取独特的行
因此,如果您想比较两个流,以下将仅发出第一个文件独有的行:
comm -23 <(xml_to_tsv <one.xml | sort) <(xml_to_tsv <two.xml | sort)
...以下将仅发出第二个唯一的行:
comm -13 <(xml_to_tsv <one.xml | sort) <(xml_to_tsv <two.xml | sort)
做同样的事情,没有 XMLStarlet
如果您没有安装 XMLStarlet,您可以生成一个 XSLT 模板来执行相同的操作。因此,如果您有以下文件 extract_entries.xslt
:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<xsl:for-each select="//entry[@name]">
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="./@name"/>
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="./@value"/>
</xsl:call-template>
<xsl:value-of select="' '"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="value-of-template">
<xsl:param name="select"/>
<xsl:value-of select="$select"/>
<xsl:for-each select="exslt:node-set($select)[position()>1]">
<xsl:value-of select="' '"/>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
...您可以在根本没有 XMLStarlet 的系统上使用以下 xml_to_tsv
实现:
xml_to_tsv() {
xsltproc extract_entries.xslt -
}
使用以下两个数据文件和一个转换文件:
file1.xml:
<TG>
<entry name="common" value="foo"/>
<entry name="changed" value="bar"/>
<entry name="unique1" val="qux"/>
</TG>
file2.xml:
<TG>
<entry name="common" value="foo"/>
<entry name="changed" value="bar"/>
<entry name="unique2" val="quux"/>
</TG>
transform.xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- To be passed with xsltproc's stringparam argument -->
<xsl:param name="other"/>
<!-- Convenience aliases -->
<xsl:variable name="file1" select="/"/>
<xsl:variable name="file2" select="document($other)"/>
<xsl:template match="/">
<results>
<common_entries>
<xsl:for-each select="$file1/TG/entry[@name]">
<xsl:variable name="node1" select="."/>
<xsl:variable name="node2" select="$file2/TG/entry[@name=$node1/@name]"/>
<!-- xpath 1.0, the only version people use, lacks the deep-equal() function -->
<xsl:if test="$node1/@value = $node2/@value">
<xsl:apply-templates select="$node1"/>
</xsl:if>
</xsl:for-each>
</common_entries>
<changed_entries>
<xsl:for-each select="$file1/TG/entry[@name]">
<xsl:variable name="node1" select="."/>
<xsl:variable name="node2" select="$file2/TG/entry[@name=$node1/@name]"/>
<xsl:if test="$node1/@value != $node2/@value">
<diff>
<old>
<xsl:apply-templates select="$node1"/>
</old>
<new>
<xsl:apply-templates select="$node2"/>
</new>
</diff>
</xsl:if>
</xsl:for-each>
</changed_entries>
<unique1_entries>
<xsl:for-each select="$file1/TG/entry[not(@name=$file2/TG/entry/@name)]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</unique1_entries>
<unique2_entries>
<xsl:for-each select="$file2/TG/entry[not(@name=$file1/TG/entry/@name)]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</unique2_entries>
</results>
</xsl:template>
<!-- Standard identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
然后 运行 xsltproc --stringparam other file2.xml transform.xslt file1.xml
将产生:
<?xml version="1.0"?>
<results>
<common_entries>
<entry name="common" value="foo"/>
</common_entries>
<changed_entries>
<diff>
<old>
<entry name="changed" value="bar"/>
</old>
<new>
<entry name="changed" value="baz"/>
</new>
</diff>
</changed_entries>
<unique1_entries>
<entry name="unique1" val="qux"/>
</unique1_entries>
<unique2_entries>
<entry name="unique2" val="quux"/>
</unique2_entries>
</results>
我有两个文件:file1 和 file2。以下是文件内容示例:
<TG>
<entry name="KEYNAME" val="" type="string" />
<entry name="KEYTYPE" val="" type="string" />
<entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
...
</TG>
我想检查file1中的某些行(包含字符串"entry name"的行)是否存在于file2中,如果存在,则比较这两行是否相同。
我实际上是通过复制 file1 创建了 file2,然后更改了一些值。问题是比较两个字符串变量并没有 return 正确的值。虽然在显示这两个变量时我可以看到它们是相同的,但比较的结果表明它们不是。我正在使用 Ksh。这是我的代码:
while read p; do
if [[ $p == *"entry name"* ]]; then
PARAM_NAME=$(echo $p | cut -d '"' -f2)
echo $PARAM_NAME
PARAM_OLD=$(grep $PARAM_NAME file2)
if [[ $PARAM_OLD == *"entry name"* ]]; then
echo $PARAM_OLD
echo $p
if [ "$PARAM_OLD" = "$p" ]; then
echo 'Identical values'
else
echo 'Different values'
fi
else
echo "$PARAM_NAME does not exist in previous version file. Using default value"
fi
fi
done <file1
我尝试了括号、等号和引号的所有可能性([]、[[]]、=、==、""、'""' 等)
这是我得到的输出:
<entry name="KEYNAME" val="" type="string" />
KEYNAME
<entry name="KEYNAME" val="" type="string" />
<entry name="KEYNAME" val="" type="string" />
Different values
<entry name="KEYTYPE" val="" type="string" />
KEYTYPE
<entry name="KEYTYPE" val="" type="string" />
<entry name="KEYTYPE" val="" type="string" />
Different values
<entry name="TIMEZONE_OFFSET" val="-24" type="INT16" />
TIMEZONE_OFFSET
<entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
<entry name="TIMEZONE_OFFSET" val="-24" type="INT16" />
Different values
我仍然发现字符串不同!我将不胜感激任何解释和帮助。
您的 if 语句中只有 1 个 =,请将其更改为两个:
if [ "$PARAM_OLD" = "$p" ]; then
至:
if [ "$PARAM_OLD" == "$p" ]; then
此外(现在不是问题,但可能是你的下一个问题),在下面的行中用 " 将 $PARAM_OLD 括起来:
if [[ $PARAM_OLD == *"entry name"* ]]; then
所以它变成了:
if [[ "$PARAM_OLD" == *"entry name"* ]]; then
大多数 recent/newer 操作系统都支持 ksh 和 ksh93。
对于 ksh93,我们可以使用关联数组来限制我们对每个文件进行一次传递。
首先是一些示例数据:
$ cat file1
<TG>
<entry name="KEYNAME" val="" type="string" />
<entry name="KEYTYPE" val="" type="string" />
<entry name="KEYATTRIB" val="" type="string" />
<entry name="TIMEZONE_OFFSET" val="-241" type="INT16" />
</TG>
$ cat file2
<TG>
<entry name="KEYNAME" val="" type="string" />
<entry name="KEYTYPE" val="" type="stringX" />
<entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
</TG>
ksh93 脚本:
$ cat my_comp
#!/bin/ksh93
unset pline
typeset -A pline
# pull unique list of 'entry name' lines from file2 and store in our associative array pline[]:
egrep "entry name" file2 | sort -u | while read line
do
# strip out the 'entry name' value
x=${line#*\"}
pname=${x%%\"*}
# use the 'entry name' value as the index for our pline[] array
pline[${pname}]=${line}
done
# for each unique 'entry name' line in file1, see if we have a match in file2 (aka our pline[] array):
egrep "entry name" file1 | sort -u | while read line
do
# again, strip out the 'entry name' value
x=${line#*\"}
pname=${x%%\"*}
# if pname does not exist in file2
[ "${pline[${pname}]}" = '' ] && \
echo "\npname = '${pname}' : Does not exist in file2. Using default value:" && \
echo "file1: ${line}" && \
continue
# if pname exists in file2 but line is different
[ "${pline[${pname}]}" = "${line}" ] && \
echo "\npname = '${pname}' : Identical values for pname" && \
echo "file1: ${line}" && \
echo "file2: ${pline[${pname}]}" && \
continue
# if pname exists in file2 and line is the same
[ "${pline[${pname}]}" != "${line}" ] && \
echo "\npname = '${pname}' : Different values for pname" && \
echo "file1: ${line}" && \
echo "file2: ${pline[${pname}]}"
done
运行 针对示例文件的脚本:
$ my_comp
pname = 'KEYATTRIB' : Does not exist in file2. Using default value:
file1: <entry name="KEYATTRIB" val="" type="string" />
pname = 'KEYNAME' : Identical values for pname
file1: <entry name="KEYNAME" val="" type="string" />
file2: <entry name="KEYNAME" val="" type="string" />
pname = 'KEYTYPE' : Different values for pname
file1: <entry name="KEYTYPE" val="" type="string" />
file2: <entry name="KEYTYPE" val="" type="stringX" />
pname = 'TIMEZONE_OFFSET' : Different values for pname
file1: <entry name="TIMEZONE_OFFSET" val="-241" type="INT16" />
file2: <entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
回到普通的基本 ksh:
$ cat my_comp2
#!/bin/ksh
egrep "entry name" file1 | sort -u | while read line1
do
x=${line1#*\"}
pname=${x%%\"*}
# see if we can find a matching line in file2; need to strip off leading
# spaces in order to match with line1
unset line2
line2=$( egrep "entry name.*${pname}" file2 | sed 's/^ *//g' )
# if pname does not exist in file2
[ "${line2}" = '' ] && \
echo "\npname = '${pname}' : Does not exist in file2. Using default value:" && \
echo "file1: ${line1}" && \
continue
# if pname exists in file2 but lines are different
[ "${line2}" = "${line1}" ] && \
echo "\npname = '${pname}' : Identical values for pname" && \
echo "file1: ${line1}" && \
echo "file2: ${line2}" && \
continue
# if pname exists in file2 and lines are the same
[ "${line2}" != "${line1}" ] && \
echo "\npname = '${pname}' : Different values for pname" && \
echo "file1: ${line1}" && \
echo "file2: ${line2}"
done
运行 针对示例文件的脚本:
$ my_comp2
pname = 'KEYATTRIB' : Does not exist in file2. Using default value:
file1: <entry name="KEYATTRIB" val="" type="string" />
pname = 'KEYNAME' : Identical values for pname
file1: <entry name="KEYNAME" val="" type="string" />
file2: <entry name="KEYNAME" val="" type="string" />
pname = 'KEYTYPE' : Different values for pname
file1: <entry name="KEYTYPE" val="" type="string" />
file2: <entry name="KEYTYPE" val="" type="stringX" />
pname = 'TIMEZONE_OFFSET' : Different values for pname
file1: <entry name="TIMEZONE_OFFSET" val="-241" type="INT16" />
file2: <entry name="TIMEZONE_OFFSET" val="-240" type="INT16" />
从 XML 转换为制表符分隔的 Key/Value 对
以下内容会将您的内容转换为制表符分隔的键值形式:
xml_to_tsv() {
xmlstarlet sel -t -m '//entry[@name]' -v ./@name -o $'\t' -v ./@value -n
}
从每行中提取独特的行
因此,如果您想比较两个流,以下将仅发出第一个文件独有的行:
comm -23 <(xml_to_tsv <one.xml | sort) <(xml_to_tsv <two.xml | sort)
...以下将仅发出第二个唯一的行:
comm -13 <(xml_to_tsv <one.xml | sort) <(xml_to_tsv <two.xml | sort)
做同样的事情,没有 XMLStarlet
如果您没有安装 XMLStarlet,您可以生成一个 XSLT 模板来执行相同的操作。因此,如果您有以下文件 extract_entries.xslt
:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<xsl:for-each select="//entry[@name]">
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="./@name"/>
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="./@value"/>
</xsl:call-template>
<xsl:value-of select="' '"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="value-of-template">
<xsl:param name="select"/>
<xsl:value-of select="$select"/>
<xsl:for-each select="exslt:node-set($select)[position()>1]">
<xsl:value-of select="' '"/>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
...您可以在根本没有 XMLStarlet 的系统上使用以下 xml_to_tsv
实现:
xml_to_tsv() {
xsltproc extract_entries.xslt -
}
使用以下两个数据文件和一个转换文件:
file1.xml:
<TG>
<entry name="common" value="foo"/>
<entry name="changed" value="bar"/>
<entry name="unique1" val="qux"/>
</TG>
file2.xml:
<TG>
<entry name="common" value="foo"/>
<entry name="changed" value="bar"/>
<entry name="unique2" val="quux"/>
</TG>
transform.xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- To be passed with xsltproc's stringparam argument -->
<xsl:param name="other"/>
<!-- Convenience aliases -->
<xsl:variable name="file1" select="/"/>
<xsl:variable name="file2" select="document($other)"/>
<xsl:template match="/">
<results>
<common_entries>
<xsl:for-each select="$file1/TG/entry[@name]">
<xsl:variable name="node1" select="."/>
<xsl:variable name="node2" select="$file2/TG/entry[@name=$node1/@name]"/>
<!-- xpath 1.0, the only version people use, lacks the deep-equal() function -->
<xsl:if test="$node1/@value = $node2/@value">
<xsl:apply-templates select="$node1"/>
</xsl:if>
</xsl:for-each>
</common_entries>
<changed_entries>
<xsl:for-each select="$file1/TG/entry[@name]">
<xsl:variable name="node1" select="."/>
<xsl:variable name="node2" select="$file2/TG/entry[@name=$node1/@name]"/>
<xsl:if test="$node1/@value != $node2/@value">
<diff>
<old>
<xsl:apply-templates select="$node1"/>
</old>
<new>
<xsl:apply-templates select="$node2"/>
</new>
</diff>
</xsl:if>
</xsl:for-each>
</changed_entries>
<unique1_entries>
<xsl:for-each select="$file1/TG/entry[not(@name=$file2/TG/entry/@name)]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</unique1_entries>
<unique2_entries>
<xsl:for-each select="$file2/TG/entry[not(@name=$file1/TG/entry/@name)]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</unique2_entries>
</results>
</xsl:template>
<!-- Standard identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
然后 运行 xsltproc --stringparam other file2.xml transform.xslt file1.xml
将产生:
<?xml version="1.0"?>
<results>
<common_entries>
<entry name="common" value="foo"/>
</common_entries>
<changed_entries>
<diff>
<old>
<entry name="changed" value="bar"/>
</old>
<new>
<entry name="changed" value="baz"/>
</new>
</diff>
</changed_entries>
<unique1_entries>
<entry name="unique1" val="qux"/>
</unique1_entries>
<unique2_entries>
<entry name="unique2" val="quux"/>
</unique2_entries>
</results>