【问题标题】:How to insert a pattern every n characters with input that may have new lines?如何使用可能有新行的输入每 n 个字符插入一个模式?
【发布时间】:2020-02-21 22:10:34
【问题描述】:

我正在尝试使用 sed 每 2000 个字符向文件中插入一个模式。我正在使用这个 sed 命令,但如果文件中有多行,它就不起作用,因为计数会在每一个新行上重置。

模式:' || '

sed "s/.\{2000\}/&'\n || '/g" file

如何使此命令与可能有也可能没有新行的输入一起工作?我可以接受非 sed 解决方案。

这里是每 4 个字符而不是 2000 个字符插入模式的示例。

示例输入:

aaaaaa
bbbbbb

示例输出:

aaaa'
 || 'aa
b'
 || 'bbbb'
 || 'b
  1. 计算 4 个字符(4 个 a),插入模式。
  2. 计算 4 个字符(2 个 a、换行符和 1 个 b),插入模式。
  3. 计算 4 个字符(4 个 b),插入模式。
  4. 仅剩 1 个字符 (1 b)

谢谢。

【问题讨论】:

  • 请在您的问题中添加示例输入(无描述、无图像、无链接)以及该示例输入所需的输出(无评论)。
  • 使用 GNU sed,您可以使用 sed -z "s/.\{2000\}/&'\n || '/g" file。但是,换行符将被视为一个单独的字符。
  • 添加了示例输入。 @WiktorStribiżew 解决方案完美运行,但遗憾的是我的 gnu sed 版本是 4.2.1,在 4.2.2 中添加了对 -z 的支持。
  • 更新了我的解决方案。 PS你确实错过了输出中的b(应该是6个b)为什么你得到aa\nb'\n没有4个字母?
  • 你能解释一下新行应该发生什么吗?计数应该从 0 开始,还是从上一行的剩余数字开始?

标签: bash perl awk sed


【解决方案1】:

perl -0777pe "s/(.{2000})/\$1'\n || '/gs" file

  • -0777 将整个文件作为一行处理
  • \$1 允许引用不带 shell 的捕获组使其为空,因为我必须使用双引号
  • s///gs 使用 g 重复多次,s 确保 .{4} 可以跨越换行符。
$ printf "aaaaaa\nbbbbbb\n" | perl -0777pe "s/(.{4})/\$1'\n || '/gs" 啊啊啊 ||啊啊 b' || 'bbbb' || 'b

【讨论】:

    【解决方案2】:

    使用 GNU awk 进行多字符 RS 并且一次仅将 4 个字符读入内存(而不是像其他一些解决方案需要的那样一次将整个文件读入内存):

    $ awk -v RS='.{4}' '{printf "%s", (RT=="" ? $0 : RT "\047\n || \047")}' file
    aaaa'
     || 'aa
    b'
     || 'bbbb'
     || 'b
    

    【讨论】:

    • 不错,与 perl -0777sed -z
    • awk -v RS="",当文件中没有空行时(如果有,该方法将失败)或gawk -v RS='^$',到目前为止我没有在任何答案中看到但可能会出现。
    【解决方案3】:

    这个gnu awk 可能会:

    echo "abcdefghijkl" | awk -v FS= -v OFS= '{for (i=1;i<=NF;i++) if (i>1 && i%3==1)  $i="\n ||"$i}1'
    abc
     ||def
     ||ghi
     ||jkl
    

    对于您的文件,每 1000 个字符。

    awk -v FS= -v OFS= '{for (i=1;i<=NF;i++) if (i>1 && i%1000==1)  $i="\n ||"$i}1' file
    

    更新的解决方案:

    awk -v FS= -v OFS= '{for (i=1;i<=NF;i++) if (i>1 && i%4==1)  $i="\x27\\n || \x27"$i;printf "%s\x27\\n || \x27",$0} END {print ""}' file
    aaaa'\n || 'aa'\n || 'bbbb'\n || 'bb'\n || '
    

    【讨论】:

    • 使用八进制 \047 而不是十六进制 \x27 来表示 '。见awk.freeshell.org/PrintASingleQuote。此外,与发布的预期输出相比,最终脚本在'aa 之后缺少\nb,并在输出末尾显示了一个额外的b'\n || '
    • @EdMorton 感谢 \047 提示。 OP多次更改请求,我已经放弃解决它。
    • 不客气,是的,我有一种感觉,可能就是这样!
    【解决方案4】:

    这是一种通用方法,我们可以在其中给出要插入字符串的字符数。使用 GNU awkRSFSgsub 功能。用 GNU awk 测试并且只提供了样本。(好的,我刚刚测试了在 5 个字符之后插入新字符,它也工作得很好:))

    awk -v noc="4" -v char="\047\n || \047" -v RS="" -v FS="\n" '{num=num==noc?(noc-1):noc;gsub(".{"num"}","&" char)} 1' Input_file
    

    添加非单线形式的解决方案:

    awk -v noc="4" -v char="\047\n || \047" -v RS="" -v FS="\n" '
    {
      num=num==noc?(noc-1):noc
      gsub(".{"num"}","&" char)
    }
    1
    '  Input_file
    

    输出如下:

    aaaa'
     || 'aa
    b'
     || 'bbbb'
     || 'b
    


    上面代码的解释:添加上面代码的完整解释。

    awk -v noc="4" -v char="\047\n || \047" -v RS="" -v FS="\n" '  ##Mentioning noc=4 for number of characters after which we want to insert new character\
                                                                   ## , mentioning char variable with value which OP wants to insert. \
                                                                   ##  Making RS NULL here, making FS as new line here for all lines of Input_file
    {
      num=num==noc?(noc-1):noc                                     ##Creating variable num whose value is noc-1 when it is 4 and 4 when it is NOT 4.
      gsub(".{"num"}","&" char)                                    ##Using gsub function to give number of characters which need to be substitutes with new char here.
    }
    1                                                              ##Mentioning 1 will print edited/non-edited line of Input_file.
    '  Input_file                                                  ##Mentioning Input_file name here.
    

    【讨论】:

      【解决方案5】:

      以下 sed 解决方案将起作用(使用四个字符而不是 2000):

        sed "H;1h;\$!d;x;s/.\{4\}/&'\n || '/g"
      

      解释:

      在 bash 中,我们需要对美元符号 \$ 进行转义,因为替换模式包含单引号,因此使用双引号将 sed 字符串括起来更容易。

      编辑: 正如 Ed Morton 在评论中所指出的,与其使用双引号并转义 $,不如使用单引号并替换每个嵌入的单引号 '使用'\'',获取:

        sed 'H;1h;$!d;x;s/.\{4\}/&'\''\n || '\''/g'
      

      【讨论】:

      • 使用这种双引号脚本的方法,您还需要在某些设置了一些历史选项的 shell 中转义 !。除非您需要双引号以避免陷入“逃避”地狱,否则请始终在字符串和脚本周围使用单引号 - sed 'H;1h;$!d;x;s/.\{4\}/&amp;'\''\n || '\''/g' file。当然,我并不是建议您实际使用 sed。
      【解决方案6】:

      有一个特殊的Linux命令可以通过指定的参数来分割文件。

      详细了解csplit 命令here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-09
        • 1970-01-01
        • 2015-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-09
        相关资源
        最近更新 更多