0

I'm trying to use regex in an if statement in a bash script, but I am getting different values.

The script:

#!/bin/bash

a="input2.txt"

paramCheck(){
  while read -r line; do
    d=( $line )
    e=${d[@]:1}
    for i in "$e"; do
      if [ "$i" == $[st][0-9] ]; then
         echo "$i"
      fi
    done
  done < "$a"
}

echo `paramCheck`

The text file:

add $s1 $s2 $s3
sub $t0 
sub $t1 $t0 
addi $t1 $t0 $s5

The predicted results:

$s1 $s2 $s3 $t0 $t1 $t0 $t1 $t0 $s5

The actual result was: nothing printed out.

0

2 Answers 2

1

You have to use double brackets for regex matching and escape the dollar, as it is a special bash character. Substitute

if [ "$i" == $[st][0-9] ]; then

for

if [[ "$i" = \$[st][0-9] ]]; then

Sign up to request clarification or add additional context in comments.

3 Comments

[[ = ]] uses wildcard ("glob") pattern syntax, not regular expressions. For regular expression matching, use [[ =~ ]]' instead.
@GordonDavisson maybe you're right, but why this works then? I'm not using wildcards.
Both glob patterns and RE use [] character classes, but otherwise they're quite a bit different. For instance, in an RE, "?" means "the last thing is optional", but in a glob it means "any single character" (in an RE, you'd use "." for that). In an RE, "*" means "zero or more of the last thing", but in a glob it means "any string of zero or more characters" (in an RE, you'd use ".*" for that). (And of course, there are multiple variations of both glob and RE syntax...) Another difference here is that [[ = ]] must match the whole string, but [[ =~ ]] allows substring matches.
1

Here's one way you could do this using various standard utilities:

$ cut -d' ' -f2- infile | grep -o '\$[st][[:digit:]]' | paste -sd ' '
$s1 $s2 $s3 $t0 $t1 $t0 $t1 $t0 $s
  • cut removes the first space separated column
  • grep finds all matches of the pattern and prints them one per line
  • paste gets the output on a single line

In pure Bash:

#!/usr/bin/env bash

while read -ra line; do
    for word in "${line[@]:1}"; do
        [[ $word == \$[st][[:digit:]] ]] && printf '%s ' "$word"
    done
done < 'input2.txt'
  • reads directly into an array with read -a
  • no intermediate assignment, loop directly over elements of "${line[@]:1}"
  • use [[ ]] for pattern matching, escape $, use locale-safe [[:digit:]] instead of [0-9]
  • use printf instead of echo to suppress linebreaks

Notice that this'll add a trailing blank.


A few pointers for your code:

  • d=( $line ) relies on word splitting and is subject to filename expansion; if you have a word * in $line, it'll expand to all files in the directory.
  • e=${d[@]:1} assigns the second and later elements of the array to a single string – now we don't have an array any longer. To keep the array, use e=("${d[@]:1}") instead.
  • for i in "$e" now has $e containing all the elements in a single string, and the quoting suppresses word splitting, so for the first line, this'll put all of $s1 $s2 $s3 into i instead of just $s1. The intent is probably for i in $e, but that's again subject to word splitting and glob expansion; use an array instead.
  • [ ] doesn't support pattern matching, use [[ ]] instead. $ has to be escaped.
  • Glob patterns (used here) are not regular expressions. Check the "Patterns" article in the references for a good overview of the differences.
  • Bash does understand both == and = within [ ], but == isn't portable (as in "POSIX conformant") – it's a good habit to use = instead. Within [[ ]], it's debatable what to use, as [[ ]] isn't portable itself.
  • echo `cmd` is the same as just cmd.

References:

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.