1

In bash man, the =~ operator is listed under [[ expression ]], and for expressions I should use closed brackets. This is also an accepted answer here:

(Double square brackets are required) "because =~ is an operator of the [[ expression ]] compound command."

However, my script line with =~ fails with closed brackets yet functions with open brackets. I would like to know what Bash is doing in both the open and closed bracket cases so that I might understand better how they function.


context

I would like to transfer files from $R/x/ into ppc/, where $R is a number referring to 30 directories, their labels ranging from 2.0 to 5.0 in increments of 0.1

x/ is a generic subdirectory label within $R.

Every 5th directory, ie: (2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0) I need to attach a condition for the script to pull only specific files from x/ and leave the rest behind. These files all begin with 105 and are pulled with $R/x/105*.

My script works as follows:

#!/bin/bash
#

for (( i=0;i<=30; i++ )) ;

do

R=$(echo "scale=2;2.0+0.1*$i" |bc -l)
#$R specifies a number corresponding to the directory I need

if (( $R =~ ^(2.0|2.5|3.0|3.5|4.0|4.5|5.0)$ )) ;
#This line is important. My question relates solely to this line.

then

cp -r $R/ ppc/

else

mkdir ppc/$R/ &&
mkdir ppc/$R/x &&

cp -r $R/x/105* ppc/$R/x
fi

done

So this works out fine. I just get a list of errors upon running the script for every value of $R from 2.0 to 5.0, ex for $R = 4.6:

./Script.in: line 11: ((: 4.6 =~ ^(2.0|2.5|3.0|3.5|4.0|4.5|5.0)$ : syntax error: invalid arithmetic operator (error token is ".6 =~ ^(2.0|2.5|3.0|3.5|4.0|4.5|5.0)$ ")

but the end output result in /ppc is exactly what I wanted. I transfered all the 30 directories from $R = 2.0 to $R = 5.0, and every fifth directory I selectively pulled specific files.


However, if I change my "if" condition to:

if [[ $R =~ ^(2.0|2.5|3.0|3.5|4.0|4.5|5.0)$ ]] ;
   ^^                                       ^^

ie. closed brackets, I get zero error messages; however without the arithmetic my output goes to shit. The output is as if my script were written with no if/then condition at all:

#!/bin/bash
#

for (( i=0;i<=30; i++ )) ;

do

R=$(echo "scale=2;2.0+0.1*$i" |bc -l)

cp -r $R/ ppc/

done

rm -r ppc/2.0/

so just straight copying with no conditions, only somehow I also have no 2.0 directory in ppc/, so the first $R fails to compute entirely.


Questions

  • Why does bash treat the "if" condition as arithmetic instead of say, text strings? Will I always need open parentheses any time I have a number involved even if the manual labels it as a closed bracket expression?
  • How is the "if" condition being interpreted by Bash with closed brackets? I am wondering how I got the result I did. I don't believe it entirely ignored the if condition, because the 2.0 directory did not print.
  • Why do I get those errors with the open brackets? Can I fix that?
  • Side question: the ^ in

    =~ ^(

    means "not equal to," is that correct? As in it functions like "!=~" if that operator existed? I could not find its meaning in the manual. I copied that command from online in stack. My understanding is that if I leave ^ out, the script will look for = rather than !=

Thank you!

Community
  • 1
  • 1
Blaisem
  • 557
  • 8
  • 17
  • 3
    Regex evaluation using `=~` is only supported in `[[ ... ]]` and even though your regex is not entirely correct it should work for given range of `i` in `for` loop. – anubhava Feb 16 '17 at 15:26
  • 2
    I suggest spending some time reading the man page. `(( ... ))` is a command for arithmetic expressions only. The `^` following `=~` is just the start-of-string anchor in the regular expression. – chepner Feb 16 '17 at 15:35
  • The token following an `if` is a *command* and that equates to True if the command is successful (i.e. if it has a return value of zero). The brackets, `[[`, `((`, or `[` are all different commands. `[[` is mostly used for text pattern matching and `((` is used for arithmetics. `[` is the old Bourne shell `test` command. – cdarke Feb 16 '17 at 16:14
  • 1
    Please only ask one thing at a time. Please read the [help] pages about how to ask questions here on Stack Overflow. – AdrianHHH Feb 16 '17 at 16:22
  • Thank you for the replies. @anubhava I understand =~ is for [[ ... ]]. That is why I am curious why (( ... )) works. &chepner Thank you for the clarification on ^; I thought it might be that. As mentioned, I know (( ... )) is for arithmetic expressions *only*. Why does it work for an operator the manual indicates is strictly [[ ... ]]? &cdarke My script is functioning as !~ as best I can understand? That confused me because the manual indicates =~ is for matching expressions, not the opposite. Do you know why it is working as !~ ? – Blaisem Feb 16 '17 at 16:49
  • @AdrianHHH Sorry for asking multiple questions. Is it then preferable I copy+paste repost this 4 times? I'm okay with either protocol. – Blaisem Feb 16 '17 at 16:51
  • @cdarke `!~` is not in bash.. – Grisha Levit Feb 17 '17 at 02:11

0 Answers0