5

I have a file which has contents something like this:

File.txt:

661###############20160315###
###########################
###########################
661###############20160316###
###########################
661###############20160317###
###########################

I want to split this single file into multiple files based on the the starting string "661" and date (2016MMDD) and rename the split file as 20160315.txt, 20160316.txt and so on. For example each split file will have:

20160315.txt will have:

661###############20160315########
################################
################################

20160316.txt will have:

661###############20160316########
################################

20160317.txt will have:

661###############20160317#######
###############################

Is there an awk command that can do it?

1

2 Answers 2

7

I am sure there is an awk command that can do this, I am not skilled enough in awk to come up with a solution. In the meantime, you could use something like this:

#!/bin/bash

csplit -z tosplit /661/ {*}

for file in xx*; do
    newName=$(egrep -o '2[0-9]{7}' $file)
    mv $file $newName.txt
done
rm -rf xx*

Where tosplit is this file (your example file):

661###############20160315###
###########################
###########################
661###############20160316###
###########################
661###############20160317###
###########################

After running this script (in the same directory as the tosplit file) I get three files:

ls 2016031*
20160315.txt  20160316.txt  20160317.txt

...looking like this:

cat 20160315.txt 
661###############20160315###
###########################
###########################

cat 20160316.txt 
661###############20160316###
###########################

cat 20160317.txt 
661###############20160317###
###########################

You can possibly(?) use csplit to name the files as well, but that too is above my paygrade!

4
  • 2
    Very good for using csplit. You can name the file prefix and, with GNU csplit, you can specify an extension format, but you can't do it based on the content of the files, so I doubt this will be improved upon much. csplit, not Awk, is the tool for this job.
    – Wildcard
    Nov 3, 2016 at 18:57
  • @Wildcard Thanks for the comment. I suspected that naming the files based on content would be near impossible with csplit, now I know! Nov 3, 2016 at 18:59
  • On zsh, I needed to change {*} to "{*}" to circumvent a failed shell expansion attempt. Jan 31, 2020 at 11:37
  • Whenever I need something that seems complicated I think of awk and then I come to the Unix zone here and learn about amazingly simple/powerful/useful programs like column, csplit, etc. It makes me happy, yet also so sad that I grew up on the $h!tty, capitalistic, scam called DOS instead of a real OS like Unix. Feb 25, 2022 at 21:01
3

With awk something like

awk '/^661/{f=substr($0,match($0,/2016[0-9]{4}/),8)".txt"}{print>>f}' file.txt

might work for you.

Basicly the parts are:

/^661/{...} # on each line starting with 661

match($0,/2016[0-9]{4}/) # find the index of the date (2016MMDD) in current line

substr($0,match($0,/2016[0-9]{4}/),8) # extract the the date in the current line

f=substr($0,match($0,/2016[0-9]{4}/),8)".txt" # assign it to f and append ".txt"

{print>>f} # redirect the content of the current line into the file named by f

With a traditional awk implementation you might have to replace the interval expressions to:

awk '/^661/{f=substr($0,match($0,/2016[01][0-9][0-9][0-9]/),8)".txt"}{print>>f}' file.txt

Depending on your use case you might also want to change the behavior of the redirection, i.e. print>f vs. print>>f.

1
  • This is good to know. Haven't tried this yet, but will give it a try.
    – nerd
    Nov 4, 2016 at 21:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .