【问题标题】:How to update certain positions of a text line (substring in Bash)如何更新文本行的某些位置(Bash 中的子字符串)
【发布时间】:2021-10-05 13:32:12
【问题描述】:

嗨,我是 shell 编程的新手,我面临以下问题,我正在合并两个文件,所以我需要更新代表新文件中行数的位置的页脚,原始页脚看起来如下:

90000000000100000000000012345...n
90000000000300000000000012345...n

行数表示在位置 2 到 13 例如

9[000000000010]0000000000012345...n
9[000000000030]0000000000012345...n

我正在使用以下说明

newfooter=`sed 1d $newfile | awk -v completeline=$originalfooter'{total_lines+=1}END{printf "%1d%012d%d\n", substr(completeline, 1, 1), total_lines, substr(completeline, 14)}'`

但我的结果如下:

90000000000412345...n

正如你所看到的那样,总行数的总和一次是可以的 000000000010 + 000000000030 = 000000000040 但是该行中接下来的 11 个位置正在丢失,我想要得到的结果应该是如下:

90000000000400000000000012345...n

老实说,我完全迷路了,感谢您的帮助

【问题讨论】:

  • 我认为您必须在printf 格式中为最后一部分指明零填充。类似于第二部分的内容,%012d
  • 如果我算得好,你会错过%017d

标签: bash shell sed substring


【解决方案1】:

虽然其他人建议将 %d 更改为 %012d(或 %017d),但问题要简单一些。

考虑以下几点:

awk -v x='000001' 'BEGIN{printf "%d", x}'
1                                          # leading zeros are dropped

awk -v x='000001' 'BEGIN{printf "%06d",x}'
000001                                     # leading zeros are dropped and then added back

这些示例 (%d / %06d) 告诉 printf 将参数视为整数/数字。

正如 OP 在 cmets 中提到的,真正需要的是保留构成“其余部分”的任何内容。

我们可以通过将%d 换成%s 来实现这一点,这告诉printf 它正在处理一个字符串:

awk -v x='000001' 'BEGIN{printf "%s", x}'
000001                                     # string is printed as is, no dropping/adding of 0's

这意味着 OP 应该能够通过以下方式获得所需的输出:

printf "%1d%012d%s\n" ...

事实上,前面的%1d 也可以替换为%s

printf "%s%012d%s\n" ...

注意:%s 用于未更改的“页脚”部分意味着将保留非数字(如果它们可能存在)而不是引起问题和/或者如果使用%d则被丢弃

将 OP 的 awk 代码简化为演示:

$ x='90000000000100000000000012345...n'

# current printf format:

$ awk -v x="${x}" 'BEGIN{printf "%1d%012d%d\n", substr(x,1,1), "40", substr(x,14)}'
900000000004012345

# proposed printf format:

$ awk -v x="${x}" 'BEGIN{printf "%s%012d%s\n", substr(x,1,1), "40", substr(x,14)}'
90000000000400000000000012345...n

【讨论】:

    【解决方案2】:

    sed 可能是处理字符串的更好工具

    originalfooter='90000000000300000000000012345...n'
    # format number
    num=$(printf "%012d" 57)
    # replace in original footer
    echo "$originalfooter" | sed -re "s/([0-9])[0-9]{12}(.*)/\1$num\2/" 
    
    # result: 90000000000570000000000012345...n
    

    有点不同的方法

    originalfooter='90000000000300000000000012345...n'
    num=$(printf "%012d" 57)
    newFooter=$(sed -re "s/([0-9])[0-9]{12}(.*)/\1$num\2/" <<< "$originalfooter")
    echo "$newFooter"
    

    【讨论】:

      【解决方案3】:

      上面看起来很接近(确切吗?),但我认为这也有效:

      Mac_3.2.57$newfooter=`cat newfile | awk -v completeline=$originalfooter '{total_lines+=1}; END {printf("%s%012.12d%s\n", substr(completeline, 1, 1), total_lines, substr(completeline, 14))}'`
      Mac_3.2.57$echo $newfooter
      90000000000300000000000012345...n
      Mac_3.2.57$cat newfile | wc -l
            30
      Mac_3.2.57$
      

      【讨论】:

        【解决方案4】:

        我认为您在printf 的最后一部分中缺少零填充。

        尝试更改您的printf "%1d%012d%d\n" 通过printf "%1d%012d%017d\n"

        这应该告诉printf 代码,您需要 17 位数字作为最后一个数字,如果数字小于这 17 位数字,则在左侧填充零。

        【讨论】:

        • 这个问题是我不能假设这 11 个位置总是零,想法是这 11 个位置必须与原始页脚保持相同
        • 目前您正在从原始页脚中获取 14 个字符(请参阅substr(completeline, 14))并在没有任何填充的情况下打印它们。这就是为什么,如果它们适合您的示例(12345)中的 5 位数字,它们将打印为 5 位数字。如果它们是 10 位或 14 位数字,则将打印 10 位或 14 位数字。如果你不想填充,但你总是想打印n字符,你必须使用%ndn是一个数字。
        • 问题是,我的页脚长度比示例大,所以每个位置都有一个目的,这就是为什么我的总行数不能超过我的 12 个位置,以下是零我的行表示是不同数据的一部分 [9][000000000040][000000000001234567][00015][...etc][][]... 所以我只需要用 total_lines 值更新第二段 [000000000040]用零填充 12 个位置并保持该行的其余部分不变
        • 因为在这个例子中,我的总行数后面有 11 个零,但在其他文件中,零可能更少或更多,这取决于数据,所以如果我至少再占据一个位置,我将更改所有信息在页脚中
        • 但是第三部分总是有固定长度的,不是吗?例如,您只需要读取 14 位数字(无论是否为零),然后打印它们。您的脚本正在读取 14 位数字,但在打印之前将所有零向左修剪。如果我理解正确的话。
        猜你喜欢
        • 2014-07-25
        • 2012-12-16
        • 2016-03-06
        • 1970-01-01
        • 2018-02-21
        • 2012-05-29
        • 2017-08-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多