Split text file into several ones when pattern appears, with command line in linux

I want to split a text file into several ones. One new file every time the pattern appears. Example: The pattern will be PAT

Original file content:

PAT --example html ABC DEF PAT --example html GHI JKL PAT --example html MNO PQR 

(and so on)

The original file is called original.txt I would like to get files like so:

$ cat page01.txt ABC DEF $ cat page02.txt GHI JKL $ cat page03.txt MNO PQR 

(and so on)

Ideally with commands like grep, awk... The renaming of the files is secondary, but would be a plus to help classifying them. Thanks in advance.

3 Answers

You could use awk with some redirection:

awk -F/ '/^PAT/{close(file);file = $NF; next} /./{print >> file}' foo 

The result:

$ head page0* ==> page01 <== ABC DEF ==> page02 <== GHI JKL ==> page03 <== MNO PQR 

Essentially, for each line beginning with PAT, I'm saving the last field (via a field separator of /) the variable file, and then printing every non-empty line (/./ matches lines with at least one character) to the name contained in file.

Note that it's important to close the previous file at each loop to prevent a "makes too many open files" error when there's "a lot" of file created.

10

Since @muru beat me to the awk solution, here's a Perl approach (but use @Muru's instead, it is simpler and more efficient):

perl -00ne 's#PAT.*/(.*)\n##; open($F,">","$1.txt"); s/\n\s*(\n|$)//g; print $F "$_\n"' original.txt 

The -00 makes perl treat paragraphs as lines: a "line" (a "record") is now a paragraph, defined by an empty line. s#PAT.*/(.*)\n## will remove the line starting with PAT from the record, and the parentheses capture the last word after the / as $1.Then, we open $1.txt for writing (open($F,">","$1.txt")) with the file handle $F. The next step, s/\n\s*\n//g; removes blank lines and, finally, the current record is printed to the filehandle $F with print $F "$_\n".


To use everything after the // as a name, try:

perl -00ne 's#PAT.*//(.*)\n##; $k=$1; $k=~s#[./]##g;open($F,">","$k.txt"); s/\n\s*(\n|$)//g; print $F "$_\n"' original.txt 

On your example, that would result in the following files:

askubuntucompage01.txt askubuntucompage02.txt askubuntucompage03.txt 
12

Also have a look at csplit(1):

csplit --suppress-matched --prefix page --suffix-format %02d.txt original.txt '/^PAT/' '{*}' 

Splits file orginal.txt into separate files when regex pattern found.

page00.txt page01.txt ... 
6

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like