2
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...

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
Calvin
  • 29
  • 4

2 Answers2

5

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.)

ruakh
  • 175,680
  • 26
  • 273
  • 307
2

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.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Sorry i did get what the previous reference was saying as it related to what i was trying to do. I will go back and read it again now that i think i understand it. I can see why this would be bad if you tried it with something that is changing the text. Not sure if i see the down side in this instance as if a user uses a wrong color code it will just fail to highlight anything. Is there something more damaging I am missing? – Calvin Feb 28 '20 at 23:40
  • The problem with data being substituted into code is that it's treated as *code*, not *data*, so it can have undesired side effects. Consider if `aquah='hi; system("rm -rf ~")'` -- you'd have all your files deleted by `recolor lo aquah`. Sure, you don't usually let untrusted users set variables in your shell, but get into the habit of writing this kind of code, and it's liable to be used somewhere it's handling filenames or other untrusted content. – Charles Duffy Mar 01 '20 at 00:04
  • (I've been there for a major data-loss event caused by a buffer overflow bug substituting random garbage into a filename -- random garbage that happened to contain a whitespace-surrounded wildcard, and when a shoddily-written script tried to delete that one file, it deleted *all* the archives located under the same directory of backups. Sure, that kind of thing may be rare, but when it wipes out your billing data for the month, how rare it is isn't very reassuring). – Charles Duffy Mar 01 '20 at 00:09
  • "_happened to contain a whitespace-surrounded wildcard ... tried to delete that one file, it deleted all..._" --- ugh, sorry, _that_ nightmare. a very good example unfortunately. – zdim Mar 02 '20 at 17:45