我有一个5000行文件,由行块组成,块之间有一个END字符串,如下所示
ATOM 1
ATOM 3
ATOM 25
END
ATOM 2
ATOM 36
ATOM 22
ATOM 12
END
ATOM 1
ATOM 87
END
我想找到一种方法将文件分成几个文件,每个文件在END字符串之前包含一个单独的行块。第一个文件应该如下所示:
ATOM 1
ATOM 3
ATOM 25
第二个文件应该包含
ATOM 2
ATOM 36
ATOM 22
ATOM 12
以此类推。我想过使用awk '/END/{flag=1; next} /END/{flag=0} flag' file
之类的东西来获取END字符串之间的块。然而,这对我的第一个块不起作用,因为END字符串只在块之后,最重要的是,它不能考虑到它找到字符串END的次数,以将每个块分离到其单独的文件中。是否有一种方法,我可以使用字符串END拆分我的文件成几个,每个包含一个块,以字符串END结束?
关闭。增加每个块的标志。并输出到一个文件。在awk:
awk 'BEGIN{flag=0} /END/{flag++} {print $0 > flag ".txt"}' file
在Bash:
flag=0
while IFS= read -r line; do
if [[ "$line" = "END" ]]; then
flag=$((flag + 1))
else
printf "%sn" "$line" >> "$flag.txt"
fi
done <inputfile
等在任何其他编程语言
awk
的record
分隔符(RS
)可以重置为读取以"&;end &;"分隔的块,并且每个块可以打印到文件名以数字递增的文件中,如下所示:
awk 'BEGIN{RS="END";ORS="";i=1;} {print > "part"i".file"; i++}' file.txt
输出记录分隔符ORS
已设置为空字符串,以防止在文件末尾出现额外的新行。第一部分之后的文件仍然有一个前导空行,如果必要的话可以删除。它还创建了一个额外的空文件,可以忽略这个"快速而肮脏"的解决方案。
递增计数器i
用于形成顺序文件名。
从上述过程检查的输出与输入的文件副本一起运行:
> ls part*
part1.file part2.file part3.file part4.file
> cat part1.file
ATOM 1
ATOM 3
ATOM 25
>cat part2.file
ATOM 2
ATOM 36
ATOM 22
ATOM 12
(部件。文件为空)
可能的问题:awk的某些版本显然不喜欢直接打印重定向的文件名连接。如果这里出现错误,文件名可以以稍微长一点的版本进行:
awk 'BEGIN{RS="END";ORS="";i=1;} {flname="part"i".file"; print > flname; i++}' file.txt
使用任意awk:
$ awk -v cnt=1 '
/END/ { cnt++; next }
cnt != prev { close(out); out="foo" cnt ".txt"; prev=cnt }
{ print > out }
' file
$ head foo*.txt
==> foo1.txt <==
ATOM 1
ATOM 3
ATOM 25
==> foo2.txt <==
ATOM 2
ATOM 36
ATOM 22
ATOM 12
==> foo3.txt <==
ATOM 1
ATOM 87
$ awk '/END/{c++; next} {print > ("file."(c+1)".txt")}' file
==> file.1.txt <==
ATOM 1
ATOM 3
ATOM 25
==> file.2.txt <==
ATOM 2
ATOM 36
ATOM 22
ATOM 12
==> file.3.txt <==
ATOM 1
ATOM 87
如果你有太多的节最终可能会遇到太多的文件打开的问题。因此,最好在完成后关闭文件。
$ awk 'BEGIN {f="file."(++c)".txt"}
/END/ {close(f); f="file"(++c)".txt"; next}
{print > f}' file
这可能适合您(GNU csplit):
csplit -qz -f file -b '%04d.txt' --suppress-matched file '/END/' '{*}'
保持安静,删除任何空文件。
以file
作为输出文件的前缀,以四位数字加.txt
作为后缀。
禁止匹配行,例如END
.
重复,直到文件结束。
如果您不介意文件默认为xxn
,请使用:
csplit -qz --sup file '/END/' '{*}'
其他几种方法
Perl:
perl -0777 -lnE 'while (/([sS]*?)^ENDs*/gm) {
$cnt++;
open(FH, ">file_${cnt}.txt");
print FH $1;
close (FH);
}' file
Ruby:
ruby -e 'cnt=1; s=$<.read.scan(/([sS]*?)^ENDs*/m) { |b|
File.write("file_#{cnt}.txt", b.join(""))
cnt+=1
}' file
任何awk:
awk 'BEGIN { i=1; fn=sprintf("file_%s.txt", i) }
$1=="END" { close(fn); fn=sprintf("file_%s.txt", ++i); next }
{print > fn }
' file
或者,您可以使用sed
并使用Bash处理替换(注意——这仅在文件以最后的新行正确结束时才有效)
while IFS= read -r -d $'3' block; do
(( i++ ))
printf "%s" "$block" > "file_${i}.txt"
done < <(sed '/^END[[:space:]]*$/N; s/^END[[:space:]]*/x3/' file)
以上任何一种情况都会导致:
head file_*.txt
==> file_1.txt <==
ATOM 1
ATOM 3
ATOM 25
==> file_2.txt <==
ATOM 2
ATOM 36
ATOM 22
ATOM 12
==> file_3.txt <==
ATOM 1
ATOM 87
# ^ Note final file has proper n termination