I'm sure this is a simple oversight, but I don't see it, and I'm not sure why this regex is matching more than it should:
#!/bin/bash
if [[ $1 =~ ([0-9]+,)+[0-9]+ ]]; then
{
echo "found list of jobs"
}
fi
This is with input that looks like "02,48,109,309,183". Matching that is fine
However, it is also matching input that has no final number and is instead "09,28,34,"
Should the [0-9]+ at the end dictate the final character be at least 1+ numbers?
You have to add markers for beginning (^) and end ($) of input:
#!/bin/bash
if [[ $1 =~ ^([0-9]+,)+[0-9]+$ ]]; then
echo "found list of jobs"
fi
Otherwise it matches 09,28,34, because it matches from 0 until 4, ignoring everything that follows.
Your regex only has to match somewhere in the string, not from start to end. To make it match the whole string, use the ^ and $ meta-characters:
#!/bin/bash
if [[ $1 =~ ^([0-9]+,)+[0-9]+$ ]]; then
echo "found list of jobs"
fi
(Incidentally, you don't need { and } to define a block in Bash, that's the job of then and fi)
Related
I'm trying to use the following if statement with regex but having some trouble:
if ! [[ $myText =~ ^[A-Z0-9]{5},[[:space:]]?[\ A-Za-z0-9]+$ ]]; then
echo "ERROR"
continue
fi
The objective is to allow YHG6D,test and YHG6D, test but not YHG6D, test (2 spaces and beyond).
I thought using the ? after [[:space:]] or " " would do the trick by limiting the space to either none or one as I want to do, but it doesn't work because I presume having 2 spaces also meets that match criterion. If so, how do I limit the match literally such that if there is no space or one space after the comma it runs the code but if there's more than one space after the comma it throws an error?
And also, I was advised to add the "\" in front of the [A-Za-z0-9] expression but have no idea what it does and if it is necessary.
Your problem is the \ in [\ A-Za-z0-9]+ which matches a space. If you remove it, the regex matches zero or one space between the comma and the word:
^[A-Z0-9]{5},[[:space:]]?[A-Za-z0-9]+$
as tested in https://regex101.com, this matches YHG6D,test and YHG6D, test, but it doesn't match YHG6D, test or YHG6D, test.
Also, you don't need the continue in your if statement:
if ! [[ $myText =~ [A-Z0-9]{5},[[:space:]]?[A-Za-z0-9]+$ ]]; then
echo "ERROR";
fi
Here is are some tests:
$ bash
$ myText="YHG6D,test"; if ! [[ $myText =~ [A-Z0-9]{5},[[:space:]]?[A-Za-z0-9]+$ ]]; then echo "ERROR"; fi
$ myText="YHG6D, test"; if ! [[ $myText =~ [A-Z0-9]{5},[[:space:]]?[A-Za-z0-9]+$ ]]; then echo "ERROR"; fi
$ myText="YHG6D, test"; if ! [[ $myText =~ [A-Z0-9]{5},[[:space:]]?[A-Za-z0-9]+$ ]]; then echo "ERROR"; fi
ERROR
$
The $ at the beginning of each line is the bash prompt, so copy the command from the myTest=... and paste it into a bash terminal to test.
I'm trying to find the word "PASS_MAX_DAYS" in a file using grep
grep "^PASS_MAX_DAYS" /etc/login.defs
then I save it in a variable and compare it to a regular expression that has the value 90 or less.
regex = "PASS_MAX_DAYS\s*([0-9]|[1-8][0-9]|90)"
grep output is: PASS_MAX_DAYS 120
so my function should print a fail, however it matches:
function audit_Control () {
if [[ $cmd =~ $regex ]]; then
echo match
else
echo fail
fi
}
cmd=`grep "^PASS_MAX_DAYS" /etc/login.defs`
regex="PASS_MAX_DAYS\s*([0-9]|[1-8][0-9]|90)"
audit_Control "$cmd" "$regex"
The problem is that the bash test [[ using the regex match operator =~ does not support the common escapes such as \s for whitespace or \W for non-word-characters.
It does support posix predefined character classes, so you can use [[:space:]] in place of \s
Your regex would then be:
regex="PASS_MAX_DAYS[[:space:]]*([0-9]|[1-8][0-9]|90)"
You may want to add anchors ^ and $ to ensure a whole-line match, then the regex is
regex="^PASS_MAX_DAYS[[:space:]]*([0-9]|[1-8][0-9]|90)$"
Without the end-of-line anchor you could match lines that have trailing numbers after the match, so PASS_MAX_DAYS 9077 would match PASS_MAX_DAYS 90 and the trailing "77" would not prevent the match.
This answer also has some very useful information about bash's [[ ]] construction with the =~ operator.
I believe you have a problem with your regex, please try that version:
PASS_MAX_DAYS\s*([0-9]|[1-8][0-9]|90)$
[lucas#lucasmachine ~]$ cat test.sh
#!/bin/bash
function audit_Control () {
if [[ $cmd =~ $regex ]]; then
echo match
else
echo fail
fi
}
regex="PASS_MAX_DAYS\s*([0-9]|[1-8][0-9]|90)$"
audit_Control "$cmd" "$regex"
[lucas#lucasmachine ~]$ export cmd="PASS_MAX_DAYS 123"
[lucas#lucasmachine ~]$ ./test.sh
fail
[lucas#lucasmachine ~]$ export cmd="PASS_MAX_DAYS 1"
[lucas#lucasmachine ~]$ ./test.sh
match
I can explain the problem was the other regex was not checking the end of line, so, your were matching "PASS_MAX_DAYS 1"23 so 23 were not being "counted" to your regex. Your regex was really matching part of the text.. Now with the end of line it should match exactly 1 digit find a end of line, or [1-8][0-9] end of line or 90 end of line.
I've got the following text file which contains:
12.3-456, test
test test test
If the line contains xx.x-xxx, then I want to print the line out. (X's are numbers)
I think I have the correct regex and have tested it here:
http://regexr.com/3clu3
I have then used this in a bash script but the line containing the text is not printed out.
What have I messed up?
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
if [[ $line =~ /\d\d.\d-\d\d\d,/g ]]; then
echo $line
fi
done < input.txt
You need to use [0-9] instead of a \d in Bash regex. No regex delimiters are necessary, and the global flag is not necessary either. Also, you can contract it a bit using limiting quantifiers (like {3} that will match 3 occurrences of the pattern next to it). Besides, a dot matches any character in regex, so you need to escape it if you want to match a literal dot symbol.
Use
regex="[0-9]{2}\.[0-9]-[0-9]{3},"
if [[ $line =~ $regex ]]
...
This works:
#!/bin/bash
#regex="/\d\d.\d-\d\d\d,/g"
regex="[0-9\.\-]+\, [A-Za-z]+"
while IFS='' read -r line || [[ -n "$line" ]]; do
echo $line
if [[ $line =~ $regex ]]; then
echo "match"
fi
done
regex is [any of 0-9, '.', '-'] followed by ',' followed by alphachars. This could be refined in a number of ways - e.g. explicit places before/ after '-'.
Testing indicates:
$ ./sqltrace2.sh < input.txt
12.3-456, test
match
123.3-456, test
match
12.3-456,
test test test
test test test
I'm studying bash programming , in particular the regex and I found this code:
numpat='^[+-]([0-9]+)$'
strpat='^([a-z]*)\1$'
read stringa
if [[ $stringa =~ $numpat ]]
then
echo "numero"
echo numero > output
exit ${BASH_REMATCH[1]}
elif [[ $stringa =~ $strpat ]]
then
echo "echo"
echo echo > output
exit 11
fi
and I don't understand what means \1 in this line:
strpat='^([a-z]*)\1$'
\1 is a backreference. It matches whatever was matched by the first capture group ([a-z]*).
So the pattern ^([a-z]*)\1$ matches a string that built from a substring that's repeated twice, such as foofoo. The capture group matches the first foo, and the backreference matches the second foo. But if the string is foobar, the backreference never matches anything, because it can't find another repetition of any of the initial strings.
You can allow any number of repetitions by using the + quantifier after \1. This matches it one or more times.
DEMO
On cygwin, which uses newlib, \1 matches only 1.
if [[ a1 =~ $strpat ]]; then echo YES; fi # YES
if [[ "$len" -lt "$MINLEN" && "$line" =~ \[*\.\] ]]
This is from Advanced bash scripting guide "Example 10-1. Inserting a blank line between paragraphs in a text file"
As I understand this matches "any string or a dot character". Right ?
It matches zero or more open bracket characters (\[*), followed by a period and a close square bracket (\.\]). Note that it only requires that a match exist somewhere in "$line", not that the whole string match. Here's a demo:
$ showmatch() { [[ "$1" =~ \[*\.\] ]] && echo "matched: '${BASH_REMATCH[0]}'" || echo "no match"; }
$ showmatch "abc[.]def"
matched: '[.]'
$ showmatch "abc.]def"
matched: '.]'
$ showmatch "abc[[[[[[[.]def"
matched: '[[[[[[[.]'
$ showmatch "abc[[[[[[[xyz.]def"
matched: '.]'
$ showmatch "abc[[[[[[[.xyz]def"
no match
...and I'm pretty sure that's not what it's supposed to be doing in that example script.
It means any string ended with dot inside bracers, for example: [.]
[abc.]
Update: +1 to Gordon Davisson, who has summed it up pretty well... so I've redacted my original post
In brief: You can test the result of a bash regex match like this:
[[ "[*.]" =~ \[*\.\] ]] ; echo ${BASH_REMATCH[0]}