0

I'm having difficulty using variables inside a perl command.

What I'm trying to do is convert a perl command to a bash script. The bash script would be used to search any given file for a given regex pattern. The bash script should first request which file to open and then request the regex pattern.

I already have a working command line and everything I tried to convert it to a bash script didn't work... I don't have much experience with command lines and bash script and I have read a lot on the internet and nothing seems to work.

#!/bin/bash

read -p "Enter the path to the file : " file_path
read -p "Enter the regular expression : " reg_exp

perl -ln0777e 'my $count=1; print "===================== RESULTS ====================="; while (/'"${reg_exp}"'/g) {printf("[%02d] Offset: 0x%x length: %dB\n     Position: %d to %d \n     Hex match: %s\n     Original: %s\n", $count++, $-[ 0 ], length $1, $-[ 0 ], $-[ 0 ] + length( $1 ) - 1, unpack("H*",$1), $1)}' "${file_path}"

When I try to use the variables inside the regex, it doesn't seem to be interpreted as a variable...

This is what the result should look like: enter image description here

My command line is this:

perl -ln0777e 'my $count=1; print "===================== RESULTS ====================="; while (/REGULAR_EXPRESSION/g) {printf("[%02d] Offset: 0x%x length: %dB\n     Position: %d to %d \n     Hex match: %s\n     Original: %s\n", $count++, $-[ 0 ], length $1, $-[ 0 ], $-[ 0 ] + length( $1 ) - 1, unpack("H*",$1), $1)}' SOURCE_FILE

SOLUTION:

Here is the working code that I came up with. Thank you Ikegami for your help!

#!/bin/bash
read -rp "Enter the path to the file : " file_path
read -rp "Enter the regular expression : " reg_exp

perl -sn0777e'
   while (/$reg_exp/g) {
      printf "[%1\$02d] Matched %2\$d bytes from position %3\$d (0x%3\$x) to %4\$d (0x%4\$x)\n",
         ++$count, $+[0]-$-[0], $-[0], $+[0]-1;
      printf "     Hex: %s\n", unpack("H*", $&);
      printf "     Match: %s\n", $&; 
   }
' -- -reg_exp="${reg_exp}" -- "${file_path}"

1 Answers1

3

The snippet attempts to generate Perl code, but it does so incorrectly. This is known as a code injection bug.

The easiest way to fix this is to avoid generating Perl code at all. This other answer suggests ways to pass data to a Perl one-liner. We'll use the second one here.

perl -sn0777e'
   while (/$reg_exp/g) {
      printf "[%1\$02d] Matched %2\$d bytes from position %3\$d (0x%3\$x) to %4\$d (0x%4\$x)\n",
         ++$count, $+[0]-$-[0], $-[0], $+[0]-1;
      printf "Match: %s\n", $&; 
      printf "Hex: %s\n", unpack("H*", $&);
   }
' -- -reg_exp="$reg_exp" -- "$file_path"

I made a few changes:

  • I removed the (unverified) expectation for the pattern to be embedded in a capture by using a combination of $& and $+[0] instead of $1 (and length($1)).
  • I made the output cleaner and more self-consistent.
  • I made the code more readable.

Note that you can get weird output for 0-characters matches (e.g. 0 bytes from position 6 to 5). For this reason, an exclusive end position is often used ($+[0] instead of $+[0]-1). I left this unchanged since 0-character matches are unlikely and inclusive positions are often used as well.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • If I insert the values directly instead of the variables, this code works... But when using the variables, it doesn't produce the expected result. It's as if it can't produce a command line dynamically. – René Chenard Jun 03 '19 at 01:38
  • Right, and I explained why that was the case, I explained how to fix it, and I provided the fix for it. (There was a small problem with the output I just fixed.) – ikegami Jun 03 '19 at 01:42
  • 1
    Okay... I have found what went wrong! :D Apparently, using "read -p" eats the backslashed characters... I had to use "read -rp" in order to keep them. – René Chenard Jun 03 '19 at 15:04
  • And now you know why it's important to also provide inputs when demonstrating a problem. Note that you still need the fix I provided – ikegami Jun 04 '19 at 00:28