BASH 脚本在文件中按位置查找字符串,匹配,然后修改该位置并在存在时插入

BASH Script to find a string in a file by position, match, then modify that position and insert if it exists

我在一个文件 (input.in) 中有几行可能看起来像这样(星号不是文字;添加是为了强调):

200928,121546,00002,**0000004015K**,**0000000641}**,00102020
200928,121546,00002,**0000000227B**,**0000000970R**,84839923
200928,121546,00003,**0000001197A**,**0000000227B**,93877763

我需要能够找到第四个和第五个元素中最后一个字符的值(或查看位置 31 和 43)以确定实际数字应该是多少以及它是正数还是负数。修改后的结果应该如下所示:

200928,121546,00002,-00000040152,-00000006410,00102020
200928,121546,00002,00000002272,-00000009709,84839923
200928,121546,00003,00000011971,00000002272,93877763

我能够使所有正数转换正常工作,但我在负数转换方面遇到问题。

我的代码看起来有点像这样以获得正向开关(顺便说一句,这是一个“打包字段”转换):

sed -i -E "s/^(.{$a})\{//" input.in

这是针对 { 正例,其中 sub 将为 0。

其中 $afor a in 30 42 do 循环引入。我没有识别和更新该字符串的最后一个字符的问题,但我无法弄清楚如何 only 如果找到相应的字符则翻转负值。我在想一些事情,比如查看整个 11 组(第 4 个和第 5 个元素),如果该组中的最后一个字符是 }JKLMNOPQR,则在第一个位置插入 - 并替换 }JKLMNOPQR0123456789. 分别。虽然卡在这里。当然 objective 是在 subs 完成后用更改更新文件。

代码示例:

    input="input.in"
        for a in 30 42
            do
                while IFS= read -r line
                do
                echo "${line:$a:1} found, converting"
                edbvalue=${line:$a:1}
                case $edbvalue in
                        {)
                        echo -n -e "{ being replaced with 0\n"
                        sed -i -E "s/^(.{$a})\{//" input.in
                        ;;

                        A)
                        echo -n -e "A being replaced with 1\n"
                        sed -i -E "s/^(.{$a})A//" input.in
                        ;;
                        .
                        .
                        .
                        R)
                        echo -n -e "R being replaced with 9\n"
                        sed -i -E "s/^(.{$a})R//" input.in
                        ;;

                        *)
                        echo -n -e "no conversion needed\n"
                        ;;
                esac
                done < "$input"
            done
            

重复重写输入文件效率极低。您想一次完成所有替换。

sed 一旦你开始做一些不平凡的事情就很难阅读,所以我建议切换到 Awk(或者如果你想在这方面投入更多的话,或者像 Python 这样的现代脚本语言).

awk -F , 'BEGIN { OFS=FS
    pos = "{ABCDEFGHI"; neg = "}JKLMNOPQR";
    for (i=0; i<10; ++i) { p[substr(pos, i+1, 1)] = i; n[substr(neg, i+1, 1)] = i }
}
{ for (i=4; i<=5; i++) {
    where = length($i)
    what = substr($i, where, 1)
    if (what ~ "^[" pos "]$") sign = ""
    else if (what ~ "^[" neg "]$") sign = "-"
    else print "Error: field " i " " $i " malformed" >"/dev/stderr"
    $i = sign substr($i, 1, where-1) (sign ? n[what] : p[what])
    }
}1' input.in

演示:https://ideone.com/z8wK0V

这不是很明显,但这里有一个快速细分。

BEGIN 块中,我们创建两个关联数组,这样

p["{"] = 0, n["}"] = 0
p["A"] = 1, n["J"] = 1
p["B"] = 2, n["K"] = 2
p["C"] = 3, n["L"] = 3
p["D"] = 4, n["M"] = 4
p["E"] = 5, n["N"] = 5
p["F"] = 6, n["O"] = 6
p["G"] = 7, n["P"] = 7
p["H"] = 8, n["Q"] = 8
p["I"] = 9, n["R"] = 9

(我们还将 OFS 设置为 FS 以便 Awk 打印输出 comma-separated,就像它读取输入一样。)

在主块中,我们遍历字段 4 和 5,提取最后一个字符并将其映射到两个数组中正确之一的相应条目,并在必要时添加一个符号。

这只是写入标准输出;保存到新文件并将其移回原始输入文件,或者如果您有 GNU Awk,请浏览其 -i inplace option.

如果您真的想在 sed 中执行此操作,它提供了一个相当方便的方法 y/{ABCDEFGHI/0123456789/ 但是拆开字段然后在完成后重新组装线并不是一件令人愉快的事情。