Hopefully this will work for you.
#! /usr/bin/env perl
use strict;
use warnings;
use 5.010;
use autodie;
my($in_file,$filter,$out_file);
if( @ARGV == 0 ){
  die "Must have filter at least\n";
}elsif( @ARGV == 1 ){
  ($filter) = @ARGV;
}elsif( @ARGV >= 2 ){
  ($in_file,$filter) = @ARGV;
}else{
  ($in_file,$filter,$out_file) = @ARGV;
}
{
  # autodie checks open() for errors
  # so we don't have to
  my($IN,$OUT);
  if( defined $in_file ){
    open $IN,  '<', $in_file;
  }else{
    $IN = *STDIN{IO};
  }
  if( defined $out_file ){
    open $OUT, '>', $out_file;
  }else{
    $OUT = *STDOUT{IO};
  }
  ProcessFiles($IN,$OUT,$filter);
  close $OUT;
  close $IN;
}
sub ProcessFilter{
  my($filter,$str) = @_;
  my @elem = grep {$_} split ' ', $str;
  $filter =~ s/\$(?|(?:{(\d+)})|(\d+))/ $elem[$1-1] /eg;
  return $filter;
}
sub ProcessFiles{
  my($IN,$OUT,$filter) = @_;
  while( my $line = <$IN> ){
    chomp $line;
    next unless $line;
    $line = ProcessFilter($filter,$line);
    say {$OUT} $line;
  }
}
It is called in one of the following manners
perl program.pl <input-file> 'filter string' <output-file>
perl program.pl <input-file> 'filter string' # sends to STDOUT
perl program.pl 'filter string' # recieves from STDIN, sends to STDOUT
If called like this 
program.pl FILE1 'stringaa ${1} stringbb $2'
it reads FILE1 and outputs:
stringaa ABC stringbb 123
stringaa DEF stringbb 456
stringaa GHI stringbb 111