I want a Perl one-liner that checks whether the first fields of an input file is the file's name and, if it isn't, adds the file name as the first column on every line.

Example written in shell :

for f in *file*.csv;
  file_column=`cat ${f} | awk -F',' '{print$1}'`
  if [ $file_column != ${f} ]
    sed -i "s/^/$f,/" $f 2>/dev/null;

But the approach above, which checks whether the file name is present in the first column and adds it if it isn't, is taking ~3 Hours for 4 Laks files. I understand that Perl is faster for file operations.

The Perl command I tried:

perl -p -i -e 's/^/Welcome to Hell,/' file*.csv

Please help me add the logic to check whether the field exists already and only change if it doesn't.

Input : file1.csv 

Output: file1.csv 

or if here is any faster way please suggest. Perl one liner because it's part of another shell script so tiny call will be better i guess (suggest please)

  • Can you give some sample input/output? Also: Why is a one liner desirable?
    – Sobrique
    Commented Jun 24, 2015 at 10:40
  • I would offer - embedding perl into another script isn't as useful as just writing a script in perl.
    – Sobrique
    Commented Jun 24, 2015 at 10:55
  • @Sobrique , i would be happy if you offer an perl script for the above problem
    – William R
    Commented Jun 24, 2015 at 11:58

5 Answers 5


Here's your perl one-liner: it works with multiple file arguments

perl -i -pe '/^$ARGV,/ or print "$ARGV,"' file1 file2 ...

$ARGV is the magic variable that holds the filename of the current file.
See http://perldoc.perl.org/perlvar.html#Variables-related-to-filehandles

The field separator (comma) is hardcoded. You can decide if that's a problem.

Small performance improvement:

perl -i -pe 'index($_, "$ARGV,") == 0 or print "$ARGV,"' file1 file2 ...

Before told about perl speed try to speed up your own script

for f in *file*.csv;
    sed -i "/^$f,/! s/^/$f,/" "$f"
  • other than first line it removed rest of the line in the file ,i need file name along with the data as starting column
    – William R
    Commented Jun 24, 2015 at 11:00
  • @WilliamR I have edited already
    – Costas
    Commented Jun 24, 2015 at 11:03
  • Thanks lot @Costas , It took 20 seconds for 30K file, Let me check for 4Laks files.
    – William R
    Commented Jun 24, 2015 at 11:05
  • It's taking long time to complete for 4 Laks files , For smaller amount like 30K files it's taking ~5 to 10 seconds. Do you have any suggestion ?
    – William R
    Commented Jun 24, 2015 at 11:56

While you can actually do this with Perl, the syntax is not the simplest (or at least, it isn't with the best I can come up with). It will probably be both simpler and faster to use other tools. For example,

  1. sed

  2. gawk (relatively recent versions)

    for f in file*csv; do  
        awk -i inplace -F, '{
                              if($1==FILENAME){print} else{print FILENAME","$0}
                            }' "$f"; 

OK, the problem with a 'perl one liner' as you note:

perl -p -i -e 's/^/Welcome to Hell,/' file*.csv

This applies a transform to the file right enough, but perl 'handles' opening the file(s) and streaming them through STDIN automagically. Which means you don't know your file name when you're doing it.

The in place edit option (-i) is a convenience but actually becomes rather more difficult to actually use effectively, since you're potentially opening a file for reading and writing concurrently.

Anyway, I'd approach your problem like this:


use strict;
use warnings;

use Text::CSV;

my $csv = Text::CSV->new( { binary => 1 } );

foreach my $filename ( glob("*.csv") ) {
    open( my $output, ">", "new.$filename.csv" ) or warn $!;
    open( my $input, "<", "$filename.csv" ) or warn $!;
    while ( my $row = $csv->getline($input) ) {
        if ( not $row->[0] eq m/$filename/ ) {
            unshift( @{$row}, $filename );
        $csv->print( $output, $row );

It uses the Text::CSV module, because actually CSV is often more complicated than just "split on comma" (think multi-line fields, and commas in text).


Can't manage a one liner, but here's a perl script. Put it in a file and make it executable. Then give it the *.csv filenames as args. It creates *.new files. If you are confident it works, uncomment the rename command at the end.

use strict;
foreach my $file(@ARGV){
    open(F,$file) or die "$file:$!";
    $_ = <F>;
    next if $_=~/^$file,/;
    open(OUT,">$file.new") or die;
    my $add = "$file,";
    print OUT $add,$_;
        print OUT $add,$_;
    close OUT;
    close F;

You must log in to answer this question.

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