【问题标题】:Removing first n character's from text file in Bash Unix从 Bash Unix 中的文本文件中删除前 n 个字符
【发布时间】:2022-09-27 17:56:08
【问题描述】:

我正在尝试从文本文件中删除前 N 个字符,重要的是 它不是逐行完成的。

目前,我编写的这段代码从每行中删除了 \'i\' 字符数。但我想从全文中删除。

for FILE in *; 
    do  x=$(wc -c < \"$FILE\"); for ((i=1; i <= $x; ++i));
            do sed \"s/^.\\{$i\\}//\" $FILE > $i; 
        done;
done;

例如,我在 xml/root.xml 目录中有这个 xml 文件

<ticket id=\"usa-001\" REFUND=\"NO\" TEST=\"TEST\">
        <airline>Us Airlines</airline>
        <emptytag id=\"usa-001\" REFUND=\"NO\" TEST=\"TEST\"/>
        <preis>30</preis><seat>
            <allseats>120</allseats>
</ticket>

我想要的是删除前 N 个字符并将其保存到一个新文件中。让我们说 5 所以它会是

et id=\"usa-001\" REFUND=\"NO\" TEST=\"TEST\">
        <airline>Us Airlines</airline>
        <emptytag id=\"usa-001\" REFUND=\"NO\" TEST=\"TEST\"/>
        <preis>30</preis><seat>
            <allseats>120</allseats>
</ticket>
  • 如果您要删除 100 个字符但第一行只有 35 个字符,应该怎么办?您会继续删除字符直到删除 100 个字符吗?您是否将行尾的换行符 (\\n)(以及可能的回车 - \\r)算作 100 个字符的一部分?
  • 要跳过 $n 字符,请使用 dd bs=1 skip=\"$n\"
  • 您添加了一个示例很好,但是既然您说whats Important is that it is done NOT LINE BY LINE 您的示例对于测试潜在解决方案会更好,如果要删除的文本不是全部在 1 行上。对于给定的示例,执行和不执行您想要的操作的脚本将产生相同的输出,因此我们无法通过使用您的示例进行测试来判断它们是否真的有效。下次有什么要考虑的。
  • 顺便说一句,您的脚本看起来您可能想要创建单独的文件以连接所有输入文件中的各种字符串 - 如果是这样,在第二个嵌套循环中调用 sed 的 shell 循环将比调用 awk 一次非常慢。如果您需要有关更大问题的帮助,请发布一个新问题。

标签: unix awk sed char


【解决方案1】:

使用 GNU sed:

$ sed -Ez 's/^.{5}//' root.xml > 5

$ cat 5
et id="usa-001" REFUND="NO" TEST="TEST">
        <airline>Us Airlines</airline>
        <emptytag id="usa-001" REFUND="NO" TEST="TEST"/>
        <preis>30</preis><seat>
            <allseats>120</allseats>
</ticket>

如果您想删除少于 5 个字符的文件中最多 5 个字符,请使用 {1,5} 而不是 {5}

【讨论】:

  • -z 并不是严格意义上的输入文件,它会将记录分隔符更改为 ASCII NUL,所以像 printf 'a\0b\0c\0d3567\n3223\n' 这样的东西不会改变......但这对于给定样本的 OP 来说可能不是问题..
  • sed 是一种处理文本的工具,根据 POSIX 定义,文本文件不得包含 NUL。
【解决方案2】:

使用您显示的示例,请尝试遵循awk 代码。用 GNU awk 编写和测试。

对于单个 Input_file:

awk -i inplace -v RS='^.{5}' -v ORS='' 'END{print}'  Input_file

对于多个 Input_file(s)使用 GNU awk:在此处使用 ENDFILE 函数,该函数将处理每个 Input_file 末尾的所有行,如名称所示。

awk -i inplace -v RS='^.{5}' -v ORS='' 'ENDFILE{print}' *

【讨论】:

    【解决方案3】:

    如果你真的只是想过滤掉文件的前 n 个字符,你想要的工具是dd,它允许你指定要跳过的块数。如果您希望块大小为 1,请使用 bs 指定。例如,要跳过输入文件的前 2 个字符,请使用:

    $ echo foobarbaz | dd bs=1 skip=2 2> /dev/null
    obarbaz
    

    您可以使用if 指定输入文件,但重定向可能更简单。 dd 将一堆诊断信息写入 stderr,输出重定向只是为了抑制这些消息。这会像泥土一样慢,因为块大小太小了,但是(如果你有一个支持这个的 dd )你可以比sed 快得多:

    dd iflag=skip_bytes skip=5
    

    【讨论】:

      【解决方案4】:

      您也可以使用tail

      # display from 4th byte
      # in other words, remove first 3 bytes
      $ printf 'apple\nbanana\nfig\ncherry\n' | tail -c +4
      le
      banana
      fig
      cherry
      

      【讨论】:

      • 请注意,这项工作与 ASCII 文件一样,在处理 UTF-8 文件时可能会出现故障。
      【解决方案5】:

      cut

      n=5; cut -c$n- file.txt
      

      看起来您想将每一行保存在一个文件中。

      n=5; cut -c$n- file.txt | awk '{print $0 > NR}'
      

      n=5; cut -c$n- file.txt | awk '{print $0 > NR; exit}'
      

      【讨论】:

      • 这可行,但它会从每一行中删除一个字符。我想从头删除 n 个字符
      • @Ali,根据您的要求更新了答案
      【解决方案6】:

      你知道的,你也可以使用hexdump

      hexdump -s 5 -ve '/1 "%c"' inputfile > outfile
      

      【讨论】:

        【解决方案7】:

        可以做一些像这样的丑陋和丑陋的事情 -

        awk 'BEGIN{ left=100 } { if (left>0) { len=length($0); if (len<left) { left-=len+1; next } else {  print substr($0,left); len=0; next } } else print $0 }' infile
        

        不要,请...改用 Ed 的sed

        你可以使用Perl -

        perl -e 'seek(STDIN,100,0) && print <>' < infile # simpler
        perl -e '$/=undef; open(my $fh,$ARGV[0]); seek($fh,100,0) && print <$fh>' infile # cleaner
        

        但威廉的dd 无需任何代码即可处理二进制文件...

        dd bs=1 skip=100 < infile > outfile 
        

        Sundeep 的可能是最估计的正好对于文本文件,如果您的版本理解 + 选项 -

        tail -c +101 infile # start at byte 101, having skipped the first 100
        

        【讨论】:

          【解决方案8】:

          如果你知道它是 ASCII :

          jot -s '' 27 | gsed -zE 's/.{15}/&\n/g; s/[\n]+$/\n/g'
          
          123456789101112
          131415161718192
          021222324252627
          
          mawk 5 RS='^.....' ORS=
          
          6789101112
          131415161718192
          021222324252627
          

          假设它符合 UTF-8 文本,那么这应该可以预先清理多达 5 个任何类型的 unicode 字符,包括换行符:

            printf '%s' "${test_input}" | gnu-wc -lcm
          
          • ꜜ&$Ꝡ*&꟠.(ꢔ2*ꥴ6,꩸:.ꮘ>0곌B2긌F4꽐J6낐N8뇄R:다V<돨Z>듈^@땼bB뗼fD뙀jF
            
          •  0      54      90
            
          mawk 'BEGIN { 
              FS = "^"(_=(_="([\0-\177]|" \
                              "[\\\302-\\\364][\\\200-\\\277]+)")(_=(_)_)_
             OFS = ORS =__="" 
             _+=_^= RS = "^$" } __!= $(NF *=_==NF )' | gnu-wc -lcm
          
          •  0      49      81
            

          【讨论】:

            猜你喜欢
            • 2013-07-10
            • 1970-01-01
            • 2020-12-25
            • 2019-05-15
            • 1970-01-01
            • 2015-10-29
            • 1970-01-01
            • 2016-10-17
            • 2014-10-10
            相关资源
            最近更新 更多