1

我想从 29 个文件中读取行并处理这些行并将它们放入 if 语句中。

在这个例子中,我创建了 3 个示例文件和一个 shell 脚本文件,它们基本上使用 while 循环读取文件,并从每个文件中读取这些行,并使用带有 if 语句的 sed 进行处理,这个 if 语句用于检查第一个变量(例如abc.def) 并将它们的值复制到文件中。

例子:

a.txt
    abc.def=123
    efg.hij=45666
    kml.nop=789
    qrs.tuv=901
    wxy.zabc=234
b.txt
    abc.def=123
    efg.hij=45666
    kml.nop=897
    klm.nop=123
    qrs.tuv=901
    wxy.zabc=234
c.txt
    abc.def=12344
    efg.hij=456
    kml.nop=123
    klm.nop=789
    wxy.zabc=234

sprict.sh

    #!/bash/bin

    count=1
    while IFS= read -r lineA && IFS= read -r lineB <&3 && IFS= read -r lineC <&4; do
    #splitting the line into two,example from line abc.def=123 slit varaibles as "abc.def" and "123"
    A1=`echo "$lineA" | sed -e 's/\=\(.*\)//' `
    A2=`echo "$lineA" | sed -e 's/^[^=]*=//' `
    B1=`echo "$lineB" | sed -e 's/\=\(.*\)//' `
    B2=`echo "$lineB" | sed -e 's/^[^=]*=//' `      
    C1=`echo "$lineC" | sed -e 's/\=\(.*\)//' `
    C2=`echo "$lineC" | sed -e 's/^[^=]*=//' `
    if [ [ "$A1" = "$B1" && "$A1" = "$C1"]];then
    echo -e "<variable id=\"$A1\">\t
    <a2>"$A2"</a2>\t
    <b2>"$B2"</b2>\t
    <c2>"$C2"</c2>\t
    </variable>\n" >> common.txt                    
    fi
    done <a.txt 3<b.txt 4<c.txt

预期输出为:

<variable id="abc.def">
   <a2>123</a2>
   <b2>123</b2>
   <c2>12344</c2>
</variable>
<variable id="efg.hij">
   <a2>456</a2>
   <b2>45666</b2>
   <c2>45666</c2>
</variable>
<variable id="kml.nop">
   <a2>789</a2>
   <b2>897</b2>
   <c2>123</c2>
</variable>
4

4 回答 4

3

下面比较任意数量的文件,并且不要求它们的内容是有序的(正如 chepner 给出的解决方案所做的那样)。它还使用 XMLStarlet 生成 XML 格式的输出,保证格式正确。

这确实意味着需要在您的系统上安装 XMLStarlet;它通常打包用于主要操作系统的现代版本。

#!/bin/bash

join_files() {
  local first
  if (( $# == 1 )); then
    sort <"$1"
  elif (( $# == 2 )); then
    join -t= <(sort <"$1") <(sort <"$2")
  elif (( $# > 2 )); then
    first=$1; shift
    join -t= <(sort <"$first") <(join_files "$@")
  fi
}

main() {
  declare -a items
  {
  printf '%s\n' '(root'
  while IFS='=' read -r -a items; do
    set -- "${items[@]}"
    name=$1; shift
    printf '%s\n' '(variable' "Aid $name"
    item_num=1
    for item; do
      printf '%s\n' "(a$item_num" "-$item" ")a$item_num"
      (( item_num++ ))
    done
    printf '%s\n' ')variable'
  done < <(join_files "$@")
  printf '%s\n' ')root'
  } | xmlstarlet depyx | xmlstarlet fo
}

main a.txt b.txt c.txt
于 2013-08-09T14:38:34.123 回答
3

您可以使用read简化将每行拆分为名称和值的过程,并使用预构建的模板printf来简化输出。此外,您的if语句中有一些语法错误:空格很重要。

shell 保留大于 10 的文件描述符供自己使用,因此您的方法不能很好地扩展到 29 个输入文件。在bash4.1 及更高版本中,您可以让bash分配文件描述符。

#!/bash/bin

# Redirect from each input file, using bash-assigned file descriptors
exec {afile}<a.txt
exec {bfile}<b.txt
exec {cfile}<c.txt

template='<variable="%s">\n\t<a2>%s</a2>\n\t<b2>%s</b2>\n\t<c2>%s</c2>\n</variable>\n'
while IFS="=" read -r -u "$afile" A1 A2 &&
      IFS="=" read -r -u "$bfile" B1 B2 &&
      IFS="=" read -r -u "$cfile" C1 C2; do

  if [[ "$A1" = "$B1" && "$A1" = "$C1" ]]; then
    printf "$template" "$A1" "$A2" "$B2" "$C2"
  fi
done
于 2013-08-09T13:54:50.973 回答
2

如果您不介意bash以外的其他语言,我可以建议awk(只是因为我熟悉它,也因为它是一个很好的文本处理)。这是我的awk解决方案,我称之为text2xml.awk

BEGIN {
    FS="=" # Use the equal sign as field separator
}

# When we encounter a new file, establish a new tag based on the file
# name
#   tag:      tag names,  (a2, b2, c2, ...)
#   tagfound: keep track of which tag has been found
#   tags:     array of tag names, where tags[0] = "a2", ...
#   tagcount: number of tags
FNR == 1 {
    tag = FILENAME
    sub(/\..*$/, "2", tag) # Replace extension with number 2, a.txt ==> a2
    if (!(tag in tagfound)) {
        tags[tagcount++] = tag
    }
    tagfound[tag] = 1
}

# For each line in each file, we parse the id, add that id to a list
# then store the value for later display.
#   id:      abc.def, efg.hij, ...
#   idfound: keep track of which id has been found
#   ids:     List of id, ids[0] = "abc.def", ...
#   values:  two-dimensional array, values[id,tag] stores the right-
#            hand-side of the equal sign
{
    sub(/^ */, "") # Remove leading spaces
    if (!($1 in idfound)) { ids[idcount++] = $1 }
    idfound[$1] = 1
    values[$1,tag] = $2
}

# Loop through the ids and tags, display the XML
END {
    for (i=0; i<idcount; i++) {
        id = ids[i]
        printf "<variable id=\"%s\">\n", id
        for (j=0; j<tagcount; j++) {
            tag = tags[j]
            printf "  <%s>%s</%s>\n", tag, values[id,tag], tag
        }
        printf "</variable>\n"
    }
}

要使用它:

awk -f text2xml.awk a.txt b.txt c.txt  # Try out with 3 files
awk -f text2xml.awk *.txt              # For all .txt files

讨论

我希望我在代码中添加了足够多的注释以对您有意义。如果需要,请随时提出更多问题。

  • ID 的顺序取决于它们在文本文件中的显示方式
  • 标签的顺序(a2,b2,c2,...)取决于命令行中文件的顺序
  • 对于那些知道awk的人,我们可以只遍历idfound数组并忘记idsand idcount。但是,这种方法并不能保证 ID 的顺序;我觉得顺序很重要。tagfound,tagcount和也是如此tags
  • 此解决方案适用于 3 个文件,它应该适用于 29 个文件及以上。
  • 我正在 Mac OS X 10.8 Mountain Lion 上测试此解决方案,但它应该适用于其他平台。

更新

根据 phani 的要求,我修复了将.txt从标签中剥离的代码。替换以下行:

    sub(/\..*$/, "2", tag) # Replace extension with number 2, a.txt ==> a2

和:

    sub(/\.txt$/, "", tag) # Remove the .txt extension
于 2013-08-09T18:31:09.473 回答
0

你快到了。您在 if 块中放错了空格。它应该是这样的:

if [[ "$A1" = "$B1" && "$A1" = "$C1" ]];then

请注意,左括号之间没有空格,右括号之前有空格。

此外,您的测试文件中有一些拼写错误。在a.txt你应该改变

klm.nop=789

kml.nop=789 

c.txt应该改变

abca.def=12344

abc.def=12344
于 2013-08-09T13:52:28.053 回答