在Bash Unix中从文本文件中删除前n个字符

3
我想从文本文件中删除前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文件在目录xml/root.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个字符并将其保存到新文件中。比如说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个字符的一部分? - markp-fuso
2
要跳过 $n 个字符,请使用 dd bs=1 skip="$n" - William Pursell
1
很好,你加了一个例子,但是由于你说“重要的是它不是逐行完成”,如果要删除的文本不全在一行上,你的例子将更适合测试潜在的解决方案。根据给定的示例,做和不做你想要的事情的脚本将产生相同的输出,因此我们无法通过测试你的示例来确定它们是否实际有效。下次请考虑这些问题。 - Ed Morton
1
顺便说一下,你的脚本看起来好像想要为来自所有输入文件的各种字符串创建单独的文件进行连接 - 如果是这样的话,使用shell循环在第二个嵌套循环中调用sed将非常慢,而不是一次调用awk。如果您需要帮助解决这个更大的问题,请发布一个新问题。 - Ed Morton
8个回答

5

如果你只想过滤掉文件的前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

使用 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>

if you want to remove up to 5 chars in files that have less than 5 then use {1,5} instead of {5}.


-z 不是严格地 slurping 输入文件,它会将记录分隔符更改为 ASCII NUL,因此像 printf 'a\0b\0c\0d3567\n3223\n' 这样的东西不会改变... 但对于 OP 给出的示例来说,这可能不是问题。 - Sundeep
1
sed是一种用于处理文本的工具,根据POSIX定义,文本文件不得包含NUL字符。 - Ed Morton
谢谢。我还有一个小问题。如果我想做相同的事情,但是最后N个字符,代码会是什么样子?基本上是这个问题的反向。 - Ali
^.{5} -> .{5}$ - Ed Morton

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

1
请注意,此功能仅适用于ASCII文件,处理UTF-8文件时可能会出现故障。 - Daweo

3

请使用您提供的示例,尝试以下awk代码。该代码在GNU awk中编写和测试。

对于单个Input_file:

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

使用 GNU awk 处理多个输入文件: 在此处使用 ENDFILE 函数,该函数将在每个输入文件的末尾处理所有行,如其名称所示。
awk -i inplace -v RS='^.{5}' -v ORS='' 'ENDFILE{print}' *

2

使用 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}'

1
这个可以工作,但它会从每一行中删除一个字符。我想要从开头删除n个字符。 - Ali
@Ali,已经根据您的要求更新了答案。 - Jetchisel

1

你知道吗,你也可以使用 hexdump 命令:

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

0

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

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

0

如果你知道它是 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
    
  • ꜜ&$Ꝡ*&꟠.(ꢔ2*ꥴ6,꩸:.ꮘ>0곌B2긌F4꽐J6낐N8뇄R:다V<돨Z>듈^@땼bB뗼fD뙀jF
    
  • 0      54      90
    
mawk2 'BEGIN { 
    FS = "^"  (_=(_="([\\000-\\177]|" \
         "[\\302-\\364][\\200-\\277]+)")(_=(_)_)_ 

   OFS = ORS =__="" 
   _+=_^= RS = "^$" } __!= $(NF *=_==NF )' | gnu-wc -lcm
  • 0 49 81
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接