This question already has answers here:
How do I escape the wildcard/asterisk character in bash?
(7 answers)
Closed 5 years ago.
I am trying to do a regular expression match with a variable that contains an asterisk.
The following set of commands in Bash does filename expansion with the asterisk in the variable on the left-hand side of the operator.
test='part1 * part2'
[[ "$test" =~ ^(.+)\ .\ (.+)$ ]] && echo $BASH_REMATCH
Results in: part1 FILE1 FILE2 part2
But it should result in: part1 * part2
I have searched and searched but cannot figure out why this is happening.
I realized while asking, the regular expression matching is working fine. There is no expansion happening inside the double brackets. The expansion is occurring after the match, when the result is echoed. The $BASH_REMATCH variable contains an asterisk, and needs to be double-quoted.
The correct set of commands is:
test='part1 * part2'
regex='^(.+) . (.+)$'
[[ "$test" =~ $regex ]] && echo "$BASH_REMATCH"
UPDATE: Set regular expression outside of test.
Related
This question already has answers here:
Checking the success of a command in a bash `if [ .. ]` statement
(1 answer)
When to wrap quotes around a shell variable?
(5 answers)
Closed last year.
I am trying to pass a regular expression as a parameter. What should I fix in my code?
My goal is to send find and the regular expression string, then use grep on the parameter so I can do whatever I want with what grep finds (which is print the count of occurrences).
This is what I send:
$ ./lab12.sh find [Gg]reen
Here's my bash code:
if [[ "$1" == "find" ]]
then
declare -i cnt=0
for file in /tmp/beatles/*.txt ; do
if [[ grep -e $2 ]] //problem is here...
then
((cnt=cnt+1))
fi
done
echo "$cnt songs contain the pattern "$2""
fi
The if statement takes a command. [[ being one, and grep is another, writing [[ grep ... ]] is essentially as wrong as writing vim grep, or cat grep etc, just use:
if grep -q -e "$pattern"
then
...
instead.
The -q switch to grep will disable output, but set the exit status to 0 (success) when the pattern is matches, and 1 (failure) otherwise, and the if statement will only execute the then block if the command succeded.
Using -q will allow grep to exit as soon as the first line is matches.
And as always, remember to wrap your paremeter expansions in double quotes, to avoid pathname expansion and wordsplitting.
Note that square brackets [...] will be interpreted by your calling shell, and you should escape them, or wrap the whole pattern in quotes.
It's always recommended use single quotes, as the only special character is another single quote.
$ ./lab12.sh find '[Gg]reen'
This question already has answers here:
Regex stored in a shell variable doesn't work between double brackets
(2 answers)
bash regex with quotes?
(5 answers)
Closed 1 year ago.
I would like someone to clarify this because I don't understand it.
Here is a sample code, that tests an argument if it is numeric or not (integer)
#/bin/env bash
pattern="^[+|-]?[0-9]+$"
[[ "$1" =~ "$pattern" ]] && echo "1:number" || echo "1:NOT number"
[[ "$1" =~ $pattern ]] && echo "1:number" || echo "1:NOT number"
it is advisable to quote always the variables, but here, if you make the test with this simple script with various inputs, you will see that if you enter a number, the quoted pattern variable returns an erroneous result (first test)
Why is that?
thanks in advance for anyone who will take the trouble to explain this to me.
Finally, sorry if that is already answered but I haven't found that particular one.
It's normally advised to quote all variables. But [[ ]] is a special operator, it parses its contents differently.
You don't need to quote variables inside double square brackets, because it doesn't do word splitting or filename expansion. But there's no harm in quoting most variables.
However, the pattern operand to =~ is treated very specially. Any part of it that's quoted is treated as a literal, not a regular expression pattern. So when you write "$pattern" it no longer does a regular expression match, it just searches for the actual characters in $pattern in $1.
This question already has answers here:
Extract filename and extension in Bash
(38 answers)
Closed 4 years ago.
I have the following bash script.
while IFS= read -r filename;
do [[ $(md5 path/to/"$filename-orig") = $(md5 path/to/"$filename") ]] || echo $filename differs;
done < path/to/list-of-files-to-compare.txt
It's supposed to compare two files (by computing their MD5 hash digest) then report if they are different. It gets the files to compare from a list.
The problem is that if the file I am trying to read is at, say,
path/to/foo-orig.js
the script will look for the file at
path/to/foo.js-orig
and, obviously, this throws an error and fails.
How do I correct this bug in my script so that I handle the .js extension correctly?
Edit
TL;DR:
Given a string foo.bar how can I get foo-orig.bar?
If I understand you correctly you are really only asking: Given a string foo.bar how can I get a string foo-orig.bar? This can be done as:
$ f="path/to/foo.js"
$ echo "${f%.js}-bak.js"
path/to/foo-bak.js
It is documented under Parameter Expansion in man bash:
${parameter%word}
${parameter%%word}
Remove matching suffix pattern. The word is expanded to produce a pattern just as in pathname expansion. If the pattern matches a trailing portion of the expanded value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the "%" case) or the longest matching pattern (the "%%" case) deleted. If parameter is # or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with # or *, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.
This example will handle all kind of extensions and even the case where there is no extension in $filename:
while IFS= read -r filename; do
if [[ $filename =~ ^(.*)([.].*)$ ]]; then
prefix=${BASH_REMATCH[1]}
suffix=${BASH_REMATCH[2]}
else
prefix=${filename}
suffix=''
fi
md5a=$(md5 -q "path/to/${prefix}-orig${suffix}")
md5b=$(md5 -q "path/to/${filename}")
[[ $md5a = $md5b ]] || echo $filename differs;
unset md5a
unset md5b
unset prefix
unset suffix
done < path/to/list-of-files-to-compare.txt
Not tested though. I just wrote it out of my head.
This question already has answers here:
Bash Regular Expression -- Can't seem to match any of \s \S \d \D \w \W etc
(6 answers)
Closed 8 months ago.
If I pass a word as an argument by:
$./file.sh hello
it gives Even as output when it should print "Argument should be a number"
#!/bin/bash
set -e
if [[ -z $1 ]]
then
echo "Argument expected"
else
if [[ $1 =~ "\D" ]] #This does not work as expected
then
echo "Argument should be a number"
else
a=$1%2
if [[ a -eq 0 ]]
then
echo "Even"
elif [[ a -eq 1 ]]
then
echo "Odd"
fi
fi
fi
#End of program
When I change "\D" to "[^0-9]" in the if statement, it works as expected and prints "Argument should be a number" to the console.
Don't they both have the same meaning? If not, in what way are the two different from each other?
Bash uses POSIX Extended Regular Expressions, not PCRE. Instead of escape sequences like \D, it uses Bracket Expressions. The bracket expression for digits is
[:digit:]
and to match non-digits, you use this inside a character class with the negation operator:
[^[:digit:]]
As you can see, this is longer than just writing [^0-9], so it's not really a shorthand. It's useful for portability to other locales, since it will include their digits as well.
Bash regex simply does not support PCRE regex syntax.
You might want to read up on different regex dialects and their history.
See e.g. Why are there so many different regular expression dialects?
The POSIX equivalent of \D is [^[:digit:]].
This question already has answers here:
Strange behavior of argv when passing string containing "!!!!"
(3 answers)
Closed 5 years ago.
Why does this command line work:
$ output='Irrelevant'; if [[ $output =~ Something ]]; then echo "I found something in the output." ; fi
And this one give me a strange parse error?
$ output='Irrelevant'; if [[ $output =~ Something ]]; then echo "I found something in the output!" ; fi
-bash: !": event not found
The only change from the first version is that the sentence to be echoed inside quotes ends with an exclamation mark. Why does Bash give me that error in the second version?
In case it matters, this is the output from bash --version:
GNU bash, version 4.2.24(1)-release (x86_64-pc-linux-gnu)
You can wrap the string in single quotes instead of double quotes.
The exclamation point invokes the very useful history expansion function described in the bash manual.
History expansions are introduced by the appearance of the history expansion character, which is ! by default. Only \ and ' may be used to escape the history expansion character.
For instance, to execute the last command that started with the word mysql type this:
!mysql
or to execute the last command containing the word grep, type this:
!?grep
The bash manual also documents the syntax of the history expansion operators.