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

问题描述

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

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

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

200928,-00000040152,-00000006410,00000002272,-00000009709,00000011971,93877763
  • {ABCDEFGHI对应于所有正字段,且子项均为0123456789
  • }JKLMnopQR对应所有负字段,且子项均为0123456789

我能够使所有正数转换正常工作,但是负数转换存在问题。

我的代码看起来像这样,以获得积极的转换(这是一个“压缩字段”转换):

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

这是{肯定的情况,其中子将为0。

$a的do循环引入for a in 30 42的位置。我没有发现和更新该字符串的最后一个字符的问题,但是如果找到相应的字符,我不知道如何翻转负值。我在想什么,就像查看整个11组(第4个和第5个元素)一样,如果该组中的最后一个字符为}JKLMnopQR,请在第一个位置插入-并替换}JKLMnopQR0123456789.分别。卡在这里。当然,目标是在完成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})\{/\10/" input.in
                        ;;

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

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

解决方法

重复重写输入文件的效率非常低。您想要一次执行所有替换。

ADD DENSITY COMPUTED BY (COUNTRIES.POPULATION / nullif(COUNTRIES.AREA,0)) 一旦开始做一些琐碎的事情就很难阅读,因此,我建议您改用Awk(如果您想对此做更多的投资,请改用Awk(或像Python这样的现代脚本语言)。

sed

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

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

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,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 块中,我们创建两个关联数组,例如

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,以便Awk将输出以逗号分隔的输出,就像读取输入一样。)

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

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

如果您确实想在FS中进行此操作,它提供了一个相当方便的sed,但是将字段分开并在完成后重新组装生产线将不会很愉快。