I have a file ABC_TabDelim.txt that contains the following:

00:00:00:00 00:00:05:00 01SC_001.jpg
00:00:14:29 00:00:19:29 01SC_002.jpg
00:01:07:20 00:01:12:20 01SC_003.jpg
00:00:00:00 00:00:03:25 02MI_001.jpg
00:00:03:25 00:00:08:25 02MI_002.jpg
00:00:35:27 00:00:40:27 02MI_003.jpg
00:00:00:00 00:00:05:00 03Bi_001.jpg
00:00:05:19 00:00:10:19 03Bi_002.jpg
00:01:11:17 00:01:16:17 03Bi_003.jpg
00:00:00:00 00:00:05:00 04CG_001.jpg
00:00:11:03 00:00:16:03 04CG_002.jpg
00:01:12:25 00:01:17:25 04CG_003.jpg

I would like to split this into multiple files for each instance of 00:00:00:00, outputting it as ABC01_TabDelim.txt, ABC02_TabDelim.txt, ABC03_TabDelim.txt, etc.

So 00:00:00:00 would indicate a new file should begin. Is there any way I can accomplish this with a Perl script?

5 Answers 5


This will work for the given format. This assumes the file will always start with 00:00:00:00.

#!/usr/bin/env perl

use strict;
use warnings;

open(my $infh, '<', 'ABC_TabDelim.txt') or die $!;

my $outfh;
my $filecount = 0;
while ( my $line = <$infh> ) {
    if ( $line =~ /^00:00:00:00/ ) {
        close($outfh) if $outfh;
        open($outfh, '>', sprintf('ABC%02d_TabDelim.txt', ++$filecount)) or die $!;        
    print {$outfh} $line or die "Failed to write to file: $!";

  • Don't use a regexp if you don't have to, use if (substr($line, 0, 11) eq "00:00:00:00").
    – goldilocks
    Feb 13, 2013 at 16:20
  • @goldilocks: what? The regex expresses the intent just fine and avoids having to count the chars (and forgetting to update something if the pattern ever changes).
    – Mat
    Feb 13, 2013 at 16:43
  • @Mat Too bad we lack a string.startswith() method that solves that problem :).
    – jordanm
    Feb 13, 2013 at 16:46
  • @Mat: the method here is a very minor difference WRT to the maintenance ("forgetting to update...") of hardcoded values (but point taken). It's not a big deal, just I would consider it poor programming because a regexp is less efficient, resource wise, and using them for everything is kinda over-the-top lazy. No offence ;) Also, using substr() is a more specific statement about what you are doing (exact match).
    – goldilocks
    Feb 13, 2013 at 16:58

Here you go. No error checking, run as, e.g., perl split file-to-munge

Update: Script cleanup as suggested by goldilocks


$n = 1;
while(<>) {
    if(/^00:00:00:00/) {
        close($out) if(n != 1);
        $fn = sprintf("ABC%02d_TabDelim.txt", $n++);
        open($out, ">", "$fn");
    print OUT;
  • -1 for use of FILE glob and 2 arg open! Sorry.
    – goldilocks
    Feb 13, 2013 at 16:24
  • 1
    @goldilocks, that is quite idiomatic Perl 5...
    – vonbrand
    Feb 13, 2013 at 16:25
  • 3
    It's actually idiomatic of Perl <5.6 (c.2000), when it was the only choice (see here: perldoc.perl.org/…) There are lots of explanations around for why this is a bad habit, (eg: securecoding.cert.org/confluence/pages/…), not the least of which is those bareword typeglob handles are by default in the global namespace regardless of where you declare them. socialtext.net/perl5/bareword_uppercase_filehandles
    – goldilocks
    Feb 13, 2013 at 16:37
  • @goldilocks, and that is relevant for a throwaway script because...?
    – vonbrand
    Feb 13, 2013 at 16:42
  • 2
    I am not downvoting but demonstrating bad practices to a new user is not a good thing. For your own throwaway, it's fine.
    – jordanm
    Feb 13, 2013 at 16:48

If the output for that sample input is expected to be 4 files, each with 3 lines, each 1st line being one starting with “00:00:00:00” and the other 2 lines the following ones, this will do it:

perl -ne 'if(/^[0:]{11}/){close F if$f;open F,sprintf(">ABC%02d_TabDelim.txt",++$f)}print F' ABC_TabDelim.txt

You don't need Perl for that: you can use the standard shell utility csplit. There's one limitation: the name of the output files has to be xxx00, xxx01, etc., so you'll need to add the desired suffix afterwards.

csplit -s -f ABC ABC_TabDelim.txt '/^00:00:00:00/' '{999999999}'
rm ABC00  # empty file (containing everything up to the first 00:00:00:00)
for x in ABC[0-9][0-9]; do mv "$x" "${x}_TabDelim.txt"; done

The {*} argument to repeat the previous delimiter as many times as possible is a GNU extension. POSIX csplit requires a first pass with grep.


You have a solution for perl, here's one way you could do it with awk:

awk '/00:00:00:00/ { out = sprintf("ABC%02d_TabDelimit.txt", ++i) } { print > out }' ABC_TabDelim.txt

If you have to split into many files you would want to close each file as you go along, prepend the sprintf function with if(out) close(out):

awk '/00:00:00:00/ { if(out) close(out); out = sprintf("ABC%02d_TabDelimit.txt", ++i) } { print > out }' ABC_TabDelim.txt

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.