we have files with the follwing file name format




we want to capture only the "FILE NAME" and "VERSION" number

so how to remove the first _ , and the last _ in the file name so we can capture the "FILE NAME" and "VERSION" , ( with sed/awk or perl one-liner )


 echo VER_collect_important_info.pl_1.0.2 | <some syntax>

 collect_important_info.pl  1.0.2
  • The XY problem is asking about your attempted solution rather than your actual problem.
    – Cyrus
    Jan 18, 2018 at 6:55

7 Answers 7


Perl comes much more naturally to me than the lighter-weight alternatives:

echo VER_collect_important_info.pl_1.0.2 | perl -pe 's/^[^_]*_(.*)_(.*)$/$1 $2/'

If it doesn't matter that this uses a heavier weight process than necessary, I'd stop there.

sed can do it however, it just feels kludgey to have to have to escape basic elements like brackets:

echo VER_collect_important_info.pl_1.0.2 | sed 's/VER_\(.*\)_\(.*\)/\1 \2/'

This should do the trick

$ echo VER_collect_important_info.pl_1.0.2 | sed 's/_/ /' | sed -r 's/(.*)_/\1 /' | awk -F" " '{print $2"\t"$3}'

The first sed is replacing the first occurrence of _ and second sed does that with the last occurrence of _ and finally awk to print

$ echo 'VER_collect_important_info.pl_1.0.2' | 
    perl -F_ -lane 'print join("_", @F[1..@F-2]), " ", @F[@F-1]'
collect_important_info.pl 1.0.2

Note: perl arrays start from 0, not 1 so the array index for the second field is [1], not [2].

This splits the input into an array (@F), separated by _ underscore characters. Then it prints the 2nd through to the 2nd-last field (@F[1..@F-2]) joined by underscores, followed by a space, and then the last field (@F[@F-1]).


find + bash solution:

Sample files:

$ ls -1 VER_*_[0-9]*[0-9]

find . -type f -name "VER_*_[0-9]*[0-9]" -exec \
        bash -c 'v=${1##*_}; f=${1#*_}; f=${f%_*}; echo $f $v' _ {} \;

The output:

collect_some_info.pl 3.4.2
collect_important_info.pl 1.0.2

With awk you can use:

echo VER_collect_important_info.pl_1.0.2 | awk -F '_' '{for (i=2; i<NF; i++) {{printf $i} if (i!=NF-1) printf "_"} printf " " $NF "\n"}'

I got mentioned result by using combination of sed and awk

echo VER_collect_important_info.pl_1.0.2 |sed -r "s/_/ /1"| sed "s/_/ /3"| awk '{print $2,$NF}'

Just run this perl script and pass the directory to look into as it's argument:


use strict;
use warnings;

my $dir = $ARGV[0];
my @files = `find $dir -type f -name "VER_*_[0-9]*[0-9]"`;

my $all_files;
foreach my $file (@files) {
    $file =~ /VER_(.*)_(.*)/;
    $all_files .= $1."  ".$2."\n";

print $all_files if ($all_files);

I ran this script by passing current directory (.) as argument to it (which contains VER_collect_important_info.pl_1.0.3).

[mask@cent ~]$ perl separate_file_version.pl .  
collect_important_info.pl  1.0.3

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.