René Chenard
René Chenard

Reputation: 55

Having difficulty creating a shell script from a command line

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}"

Upvotes: 0

Views: 95

Answers (1)

ikegami
ikegami

Reputation: 386206

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.

Upvotes: 3

Related Questions