Functions containing bash variables inserted into perl strings through variables passed through the intiiation of a function

Viewed 78
greenh='\e[2;30;42m$&\e[0m/g'
yellowh='\e[2;30;43m$&\e[0m/g'
aquah='\e[2;30;46m$&\e[0m/g'

function recolor() { 
perl -pe "s/$1/$2"
}

i use this to recolor text

EXAMPLE:

cat file.txt | recolor WHATEVERWORD $greenh

Is there a way that i can include the $ in the function so i can use it like

cat file.txt | recolor WHATEVERWORD greenh

I tried

function recolor() { 
perl -pe "s/$1/$$2"
}

function recolor() { 
perl -pe "s/$1/\$$2"
}

Both of them just break the function altogether...

2 Answers

That's really the wrong approach; you're piling a bunch of hacks together that all "leak" through to different layers. (For example, consider recolor 'x//g;#' greenh, which is intended to take occurrences of x//g;# and color them green, but which actually takes occurrences of x and deletes them; or recolor foo blueh, which is intended to take occurrences of foo and color them blue, but which actually doesn't work because your function secretly depends on a global variable being set and the user didn't define $blueh.)

I think you're better off just defining individual functions:

greenh()  { pat="$1" perl -pe 's/$ENV{pat}/\e[2;30;42m$&\e[0m/g' ; }
yellowh() { pat="$1" perl -pe 's/$ENV{pat}/\e[2;30;43m$&\e[0m/g' ; }
aquah()   { pat="$1" perl -pe 's/$ENV{pat}/\e[2;30;46m$&\e[0m/g' ; }

If you do want a single recolor function, then you're better off defining the colors inside it:

recolor() {
  perl -e '
    my $prefix =
      {
        "greenh"  => "\e[2;30;42m",
        "yellowh" => "\e[2;30;43m",
        "aquah"   => "\e[2;30;46m",
      }->{$color};
    die "Unrecognized color $color" unless $prefix;
    while (<>) {
      s/$pat/$prefix$&\e[0m/g;
      print;
    }
  ' -s -- -pat="$1" -color="$2"
}

(For completeness' sake, though, I should mention that Bash does support variable indirection; if $2 is greenh, then ${!2} is whatever $greenh is. But that feature is usually best avoided, and your example is exactly why.)

As you were already told (when your prior instance of this question was closed as a duplicate) -- to implement the replacement you're looking for in bash, without regard to what a best-practice Perl solution would be, you'd use ${!varname} to look up the variable whose name is stored in varname. In this case, you want to look up the variable whose name is stored in $2, so you'd use ${!2}:

aquah='\e[2;30;46m$&\e[0m/g'

recolor() { perl -pe "s/$1/${!2}"; }

echo "hello" | recolor lo aquah

...which successfully colors lo in aquah. See BashFAQ #6 for details.


That said: Using string concatenation to dynamically generate code is never a good idea. Don't do this. It's much safer to pass your values into perl as environment variables or separate argv elements, instead of substituting them into code.

Related