在UNIX脚本中使用模式分割文件

时间:2018-10-30 16:05:45

标签: unix

我有一个要分割的文件。该文件将具有标题和尾部记录。文件的正文包含NFD和IV记录。 IV记录是可选的。在NFD记录内,它将在某处包含“英语”或“法语”或“法语”一词。对于英语,我希望NFD和IV转到en.txt,否则转到fr.txt。
这是示例:

 1. 000000000000000;HDR;1;...
 2. 000000008651776;NFD;Individual;...;English;...
 3. 000000008651776;IV;....
 4. 000000008657876;NFD;Individual;...;English;...
 5. 000000008751796;NFD;Individual;...;French;...
 6. 000000008751796;IV;...
 7. 999999999999999;TRL;...

我希望第1、2、3、4、7行全部转到en.txt,而第1、5、6、7行全部转到fr.txt
有使用K-shell脚本的建议吗? 谢谢!

2 个答案:

答案 0 :(得分:0)

对于此任务,您需要实现状态解析。我不认为ksh仅是至关重要的解决方案。

万一需要的解决方案:

$ awk '
/^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9];HDR/ { enfile=enfile $0 RS; frfile=frfile $0 RS; }
/^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9];NFD.*;English/ { enflag=1; frflag=0; enfile=enfile $0 RS; }
/^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9];NFD.*;French/ { enflag=0; frflag=1; frfile=frfile $0 RS; }
/^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9];IV/ { if ( enflag==1 ) enfile=enfile $0 RS; if ( frflag==1 ) frfile=frfile $0 RS; }
/^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9];TRL/ { enfile=enfile $0; frfile=frfile $0; }
END { print(enfile) > "en.txt"; print(frfile) > "fr.txt"; }
' en_fr.txt

$ cat en.txt
000000000000000;HDR;1;...
000000008651776;NFD;Individual;...;English;...
000000008651776;IV;....
000000008657876;NFD;Individual;...;English;...
999999999999999;TRL;...

$ cat fr.txt
000000000000000;HDR;1;...
000000008751796;NFD;Individual;...;French;...
000000008751796;IV;...
999999999999999;TRL;...

注意:如果您在UNIX上,我没有使用正则表达式样式:/^[0-9]{15}/是为了具有本机awk兼容性。

答案 1 :(得分:0)

我相信这会做您想要的。这是在Linux上以ksh编写的,但在大多数Unix版本上都可以与bash或其他版本一起使用。

#!/bin/ksh
rm -f english.out
rm -f french.out
output=both
while read linein
do
    echo $linein | grep HDR     >/dev/null && output=both
    echo $linein | grep English >/dev/null && output=english
    echo $linein | grep French  >/dev/null && output=french
    echo $linein | grep TRL     >/dev/null && output=both
    case $output in
    both)
        echo "$linein" >> english.out
        echo "$linein" >> french.out
    ;;
    english)
        echo "$linein" >> english.out
    ;;
    french)
        echo "$linein" >> french.out
    ;;
    esac
done < data.txt

通过解释方式:

  1. 首先删除旧的输出文件。
  2. output变量设置为两者。
  3. while循环正在读取data.txt文件中,一次读取一行 放入linein变量中。 (while...done循环的输入从data.txt文件重定向。)
  4. (有些人会发现这很麻烦),我们在grep中回显每一行,丢弃输出并仅保留退出状态。如果退出状态为成功,则设置输出。如果退出状态为false,则不更改输出。这使我们可以将NFD记录发送到与先前记录相同的位置。
  5. 该案例在不同的输出值之间切换,以确定将输出发送到哪里。我希望您知道>>意味着将输出附加到文件中。注意$linein周围的引号。如果它们不存在,则不会在输入中保留空格。在您看来,这并不重要。

如果您只想在字段5中查找英语或法语,它将变得更加复杂(不适用于bash(或旧版本的ksh):

#!/bin/ksh
rm -f english.out
rm -f french.out
output=unknown
while read linein
do
    if [[ $linein == {15}(\d)\;HDR* || $linein == {15}(\d)\;TRL* ]]
    then
        output=both
    else
        if [[ $linein == {15}(\d)\;+([A-Z])\;+([^\;])\;+([^\;])\;+([^\;])\;* ]]
        then
            case ${.sh.match[5]} in
                English)
                    output=english
                ;;
                French)
                    output=french
                ;;
                *)
                    echo "unknown language: ${.sh.match[5]}" >&2
                    output=both
                ;;
            esac
        fi
    fi
    case $output in
        both)
            echo "$linein" >> english.out
            echo "$linein" >> french.out
        ;;
        english)
            echo "$linein" >> english.out
        ;;
        french)
            echo "$linein" >> french.out
        ;;
        *)
        echo "Unknown output: $output" >&2
        ;;
    esac
done < data.txt

${.sh.match[5]}在匹配字符串(括号()中的部分)中包含#5子表达式。

使用awk提取子表达式比较简单,但这是纯粹的ksh解决方案。