I am having a problem with the code below with if statements. It has three different arguments, I'm not sure if one or all of them are wrong.
#!/bin/sh
1=C
2=S
(1/2)=C <---- This might be a problem
if [ $1 == "C*" ]
then
echo "The city is:"
else [ $2 == "S*" ]
then
echo "The state is:"
else [ $1 -a $2 == "B*" ]
then
echo "The city and state is:"
else [ $1 -o $2 -ne "C*" "S*" "B*" ]
then
echo "Not found!"
fi
echo End of script
Using numbers as variables is very unwise idea. Numbers are used to represent command line parameters.
Also this construction is AFAIK unknown to bash: (1/2)=C
Also this construction:
else [ $2 == "S*" ]
then
is wrong, should be some something like:
elif [ $2 == "S*" ]
then
Also this:
else [ $1 -o $2 -ne "C*" "S*" "B*" ]
is odd, you use -ne which is for compare numbers, for strings use !=
And also a lot of other errors/problems challenging constructions I have no time to mention.
Related
I have an array of values coming from bash which i just want to check if there are numbers or not in it. It can contain -, + numbers and spaces at the start or end since bash is evaluating this as sting.
Since every number is represented with , at the end i added (,) to the regex.
Basically i want to check if element is a number or not.
The $val look like these.
[ [ -0.13450142741203308, -0.3073260486125946, -0.15199440717697144, -0.06535257399082184, 0.02075939252972603, 0.03708624839782715, 0.04876817390322685 ] ,[ 0.10357733070850372, 0.048686813563108444, -0.1413831114768982, -0.11497996747493744, -0.08910851925611496, -0.04536910727620125, 0.06921301782131195, 0.02547631226480007 ] ]
This is my code which looks at each value and evaluates each. However it doesn't seem to catch the cases.
re='^[[:space:]][+-]?[0-9]+([.][0-9]+)?(,)[[:space:]]$'
for j in ${val[*]}
do
if ! [[ "$j" =~ $re ]] ; then
echo "Error: Not a number: $j"
fi
done
Also it needs to ignore cases which throw [ or ] or ],.
Any ideas how to correct this ? Thanks for the help.
It is likely that $val is coming to you as a string.
If you don't need to validate each number as a fully legit number, you can use shell logic to filter those things that are obviously not numbers:
val='[ [ -0.13450142741203308, -0.3073260486125946, -0.15199440717697144, -0.06535257399082184, 0.02075939252972603, 0.03708624839782715, 0.04876817390322685 ] ,[ 0.10357733070850372, 0.048686813563108444, -0.1413831114768982, -0.11497996747493744, -0.08910851925611496, -0.04536910727620125, 0.06921301782131195, 0.02547631226480007 ] ]'
for e in $val; do # PURPOSELY no quote to break on spaces
e="${e/,}"
case $e in
''|*[!0-9.\-]*) printf "'%s' is bad\n" "$e" ;;
*) printf "'%s' is good\n" "$e" ;;
esac
done
Prints:
'[' is bad
'[' is bad
'-0.13450142741203308' is good
'-0.3073260486125946' is good
'-0.15199440717697144' is good
'-0.06535257399082184' is good
'0.02075939252972603' is good
'0.03708624839782715' is good
'0.04876817390322685' is good
']' is bad
'[' is bad
'0.10357733070850372' is good
'0.048686813563108444' is good
'-0.1413831114768982' is good
'-0.11497996747493744' is good
'-0.08910851925611496' is good
'-0.04536910727620125' is good
'0.06921301782131195' is good
'0.02547631226480007' is good
']' is bad
']' is bad
That is super fast but that will fail on malformed 'numbers' such as 123-456
If you do need to filter out malformed numbers, you can use awk for that:
echo "$val" | awk -v RS="[^0-9.+-]+" '($0+0==$0)'
# all legit numbers from the string...
If you populate $val with the given string, it's not an array, it's a string. Using it unquoted would apply word splitting to it which splits it into whitespace separated words. The spaces aren't part of the words, and some of the words (the last one in each bracketed sequence) don't end in a comma:
#! /bin/bash
val='[ [ -0.13450142741203308, -0.3073260486125946, -0.15199440717697144, -0.06535257399082184, 0.02075939252972603, 0.03708624839782715, 0.04876817390322685 ] ,[ 0.10357733070850372, 0.048686813563108444, -0.1413831114768982, -0.11497996747493744, -0.08910851925611496, -0.04536910727620125, 0.06921301782131195, 0.02547631226480007 ] ]'
re='^[+-]?[0-9]+([.][0-9]+)?,?$'
for j in $val ; do
if ! [[ $j =~ $re ]] ; then
echo "Error: Not a number: $j"
fi
done
To use a bash array, declare it with round parentheses and use whitespace to separate the elements:
#! /bin/bash
val=(-0.13450142741203308 -0.3073260486125946 -0.15199440717697144 -0.06535257399082184 0.02075939252972603 0.03708624839782715 0.04876817390322685 0.10357733070850372 0.048686813563108444 -0.1413831114768982 -0.11497996747493744 -0.08910851925611496 -0.04536910727620125 0.06921301782131195 0.02547631226480007)
re='^[+-]?[0-9]+([.][0-9]+)?$'
for j in "${val[#]}" ; do
if ! [[ $j =~ $re ]] ; then
echo "Error: Not a number: $j"
fi
done
I really don't know what I'm doing.
In variable a, I want to find the first appearance of '$' after the first appearance of 'Bitcoin', and print everything after it until the first newline.
I have the following code:
a = 'something Bitcoin something againe $jjjkjk\n againe something'
if [[ $a =~ .*Bitcoin.*[\$](.*).* ]]; then
echo "${BASH_REMATCH[1]}"
else
echo "no"
fi
In this example I would like to get 'jjjkjk'. All I get is 'no'.
This code might be really flawed, I have no experience in this. I think tho the problem might be with the '$' sign. Please help!
Properly handle newlines in bash with ANSI-C Quoting -- \n sequences become literal newlines.
a=$'something Bitcoin something againe $jjjkjk\n againe something'
regex=$'Bitcoin[^$]*[$]([^\n]+)'
[[ $a =~ $regex ]] && declare -p BASH_REMATCH
declare -ar BASH_REMATCH='([0]="Bitcoin something againe \$jjjkjk" [1]="jjjkjk")'
# .................................................................^^^^^^^^^^^^
To verify the contents contain newlines:
$ printf '%s' "$regex" | od -c
0000000 B i t c o i n [ ^ $ ] * [ $ ] (
0000020 [ ^ \n ] + )
0000026
Here is a working version of your code:
a='something Bitcoin something againe $jjjkjk\n againe something'
r=".*Bitcoin.*[\$]([^\n]*).*"
if [[ $a =~ $r ]]; then
echo "${BASH_REMATCH[1]}"
else
echo "no"
fi
You need to find 'Bitcoin' then find a '$' after it, no matter what is between, so you should use .* operator, also when you want to capture some text until a specific char, the best way is using [^](not) operator, in your case: [^\n] this means capture everything until \n.
Also you had an issue with your variable declaration. a = "..." is not valid, the spaces are waste. so the correct one is 'a=".."`.
Using double quotation is wrong too, this will replaces dollar sign with an empty variable (evaluation)
I am currently trying to format text for use with pandoc, but my regex replacement isn't working.
Here is my code so far:
#Importing the file
my $filename = 'example.md';
my $file = path($filename);
my $data = $file ->slurp_utf8;
#Placing code blocks into an array and replacing them in the file
my #code_block_values;
my $i = 0;
while($i > 50) {
#code_bock_values[$i] = ($data =~ /\n\t*```[^`]+(```){1}\n/);
$data =~ s/\n\t*```[^`]+(```){1}\n/(code_block)/;
$i = $i + 1;
}
#Replacing the code blocks
$i = 0;
while($i < 50) {
$data =~ s/\(code_block\)/$code_block_values[$i]/;
$i = $i + 1;
}
print $data;
$file->spew_utf8( $data );
I realize that this probably isn't the most efficient way to be doing this, but right now I'm just trying to get it working.
Basically, I am using github-flavored markdown for typing up my notes, and then trying to convert it with pandoc to a pdf file. I am doing some other formatting before-hand, but I have to extract the code blocks first (which are deliniated by triple backticks (```).)
The following is a sample code block that would be a code block:
```bash
#!/bin/bash
echo "Enter a number"
read count
if [ $count -eq 100 ]
then
echo "Example-3: Count is 100"
elif [ $count -gt 100 ]
then
echo "Example-3: Count is greater than 100"
else
echo "Example-3: Count is less than 100"
fi
```
As far as I can tell, the regex is capturing everything that I need (as tested by an online regex tester), but Perl is only inserting newlines at certain points, specifically newlines followed by a tab.
The previous example translates to:
```bash #!/bin/bash echo "Enter a number" read count if [ $count -eq 100 ] then
echo "Example-3: Count is 100" elif [ $count -gt 100 ] then
echo "Example-3: Count is greater than 100" else
echo "Example-3: Count is less than 100" fi
```
As you can see, the tabs are also completely removed. I copied over all of the file contents from atom and the different lengths of tabs are as copied over from the editor (not sure that makes a difference.) I did my editing for the shell script in vim but the editing for the notes in atom itself.
I am new to Perl, so any help would be appreciated.
My guess:
#!/usr/bin/perl
use strict;
use warnings;
# Slurps DATA after __END__ into a scalar
my $data = do { local $/; <DATA> };
my #code_block_values;
# Extract and replace code blocks with '(code_block)'
while ($data =~ s/ (``` .*? ```) /(code_block)/xs) {
push #code_block_values, $1;
}
printf "\n--| Replaced:\n%s", $data;
# Restore '(code_block)' with actual content
$data =~ s/ \(code_block\) / shift #code_block_values /xge;
printf "\n--| Restored:\n%s", $data;
__END__
```bash
#!/bin/bash
echo "Enter a number"
read count
if [ $count -eq 100 ]
then
echo "Example-3: Count is 100"
elif [ $count -gt 100 ]
then
echo "Example-3: Count is greater than 100"
else
echo "Example-3: Count is less than 100"
fi
```
```perl
#!/usr/bin/perl
print "Hello World\n";
```
Output:
--| Replaced:
(code_block)
(code_block)
--| Restored:
```bash
#!/bin/bash
echo "Enter a number"
read count
if [ $count -eq 100 ]
then
echo "Example-3: Count is 100"
elif [ $count -gt 100 ]
then
echo "Example-3: Count is greater than 100"
else
echo "Example-3: Count is less than 100"
fi
```
```perl
#!/usr/bin/perl
print "Hello World\n";
```
If you want to learn more about Perl's regular expressions, perlre and perlretut is a good read.
Hi i am try to validate user inputs to be not empty and is a number or with decimal
re='^[0-9]+$'
while [ "$num" == "" ] && [[ "$num" ~= $re ]]
do
echo "Please enter the price : "
read num
done
I was able to run smooth with just the 1st condition. When i add 2nd condition my program couldn't run.
----EDIT----------
Ok i try changing and the program run. But when i enter a number it still prompting for input.
re='^[0-9]+$'
while [ "$num" == "" ] && [ "$num" != $re ]
do
echo "Please enter the price : "
read num
done
regualar expression can be used with the operator =~ not ~= like you used it.
An additional binary operator, =~, is available, with the same
prece dence as == and !=. When it is used, the string to the right of
the operator is considered an extended regular expression and matched
accordingly (as in regex(3)). The return value is 0 if the string
matches the pattern, and 1 otherwise. If the regular expression is
syntactically incorrect, the conditional expression's return value is
2. If the shell option nocasematch is enabled, the match is performed
without regard to the case of alphabetic characters. Any part of the
pattern may be quoted to force the quoted portion to be matched as a
string. Bracket expressions in regular expressions must be treated
carefully, since normal quoting characters lose their meanings between
brackets. If the pattern is stored in a shell variable, quoting the
variable expansion forces the entire pattern to be matched as a string.
Substrings matched by parenthesized subexpressions within the regular
expression are saved in the array variable BASH_REMATCH. The element
of BASH_REMATCH with index 0 is the portion of the string matching the
entire regular expression. The element of BASH_REMATCH with index n is
the portion of the string matching the nth parenthesized subexpression.
consider theese examples (0 true/match, 1 false/no match)
re=^[0-9]+; [[ "1" =~ ${re} ]]; echo $? # 0
re=^[0-9]+; [[ "a" =~ ${re} ]]; echo $? # 1
re=^[0-9]+; [[ "a1" =~ ${re} ]]; echo $? # 1
re=^[0-9]+; [[ "1a" =~ ${re} ]]; echo $? # 0 because it starts with a number
use this one to check for a number
re=^[0-9]+$; [[ "1a" =~ ${re} ]]; echo $? # 1 because checked up to the end
re=^[0-9]+$; [[ "11" =~ ${re} ]]; echo $? # 0 because all nums
UPDATE: If you just want to check if the user inputs a number combine the lesson learned above with your needs. i think your conditions do not fit. perhaps this snippet solves your issue completely.
#!/bin/bash
re=^[0-9]+$
while ! [[ "${num}" =~ ${re} ]]; do
echo "enter num:"
read num
done
This snippet just requests input if ${num} is NOT (!) a number. During the first run ${num} is not set so it will not fit at least one number, ${num} then evaluates to an empty string. Afterwards it just contains the input entered.
Your error is simple; the variable can't be both empty and a number at the same time. Maybe you mean || "or" instead of && "and".
You can do this with glob patterns as well.
while true; do
read -r -p "Enter a price: " num
case $num in
"" | *[!.0-9]* | *.*.*) echo invalid ;;
*) break;;
esac
First off, there is the classic logic trap demonstrated in the OP's question:
while [ "$num" == "" ] && [ "$num" != $re ]
The issue here is the && which pretty much means the moment the left expression is false, the entire expression is false. i.e. the moment somebody types a non empty response, it breaks the loop and the regular expression test is never used. To fix the logic problem, one should consider changing && to ||, i.e.
while [ "$num" == "" ] || [ "$num" != $re ]
The second issue, is we are testing for negative matches to regular expression, pattern. So, this is done in two parts, one we need to use [[ "$num" =~ $re ]] for regular expression testing. Then, we need to look for negative matches, i.e. append a ! which yields:
while [ "$num" == "" ] || ! [[ "$num" =~ $re ]
Having got this far, many people observed that there is actually no need to test for the empty string. That edge condition is already covered by the regular expression itself, so, we optimize out the redundant test. The answer now reduces to:
while ! [[ "$num" =~ $re ]
In addition to the above observation, here are my notes about regular expression ( some of the observation has been collated from other answers ):
regular expressions can be tested with the [[ "$str" =~ regex ]] syntax
regular expressions match with $? == 0 ( 0 == no error )
regular expressions do not match with $? == 1 ( 1 == error )
regular expressions do not seem to work when quoted. recommend using [0-9] not "[0-9]"
To implement a number validation, the following pattern seems to work:
str=""
while ! [[ "${str?}" =~ ^[0-9]+$ ]]
do
read -p "enter a number: " str
done
You can mix regular expression filters with regular arithmetic filters for some really nice validation results:
str=""
while ! [[ "${str?}" =~ ^[0-9]+$ ]] \
|| (( str < 1 || str > 15 ))
do
read -p "enter a number between 1 and 15: " str
done
N.B. I used the ${str?} syntax ( instead of $str ) for variable expansion as it demonstrates good practice for catching typos.
I have a huge log file, where each line is a log entry with it's own timestamp. How can I retrieve logs in-between two specified timestaps (ie. start time - 22:00:00, end time - 23:00:00)?
Using bash, it is possible to generate a regex statement based only on two input timestamps, which you may pipe to a command (such as grep):
#!/bin/bash
#This is a bare-bone recursive script that accepts input of a start/end timeframe
#in the 24-hour format, based on which a regex statement will be generated that will
#select all values in between those two timeframes. Please note that this script
#has undergone some optimization (I apologize for the reading difficulty, but it
#did not occur to me until now that someone might have a use for such a script).
#You are free to use, distribute, or modify this script to your heart's content. Any
#constructive feedback is welcome. I did my best to eliminate all bugs, but should
#you find any case where the generated regex is INCORRECT for some timestamps, please
#let me know.
echo $0 - Args passed: $1 $2
START_TIME=$(echo $1 | tr -d ":")
END_TIME=$(echo $2 | tr -d ":")
DIFF_CHAR=""
REGEX=""
function loop ()
{
diffcharValue=$1
maxVal=$2
while [ $diffcharValue -lt $maxVal ]
do
REGEX="${REGEX}:[0-5][0-9]"
let diffcharValue+=2
done
}
function regexGen ()
{
diffChar=$1
start=$2
end=$3
if [ $diffChar -le 6 ]; then
regexGen $(($diffChar + 1)) $start $end 0 #the fourth arg acts as a recursion indicaton, whether the function was called recursively or not
let diffChar-=1
diffCharMinusOne=$(($diffChar - 1))
startBegin=${start:0:$diffCharMinusOne}
endBegin=${end:0:$diffCharMinusOne}
startCut=${start:$diffCharMinusOne:1}
endCut=${end:$diffCharMinusOne:1}
endStartDiff=$(($endCut-$startCut))
if [ $(($diffChar % 2)) -eq 0 ]; then
if [ $4 -eq 0 ]; then
REGEX="${REGEX}$startBegin[$startCut-9]"
loop $diffChar 6
REGEX="${REGEX}|$endBegin[0-$endCut]"
loop $diffChar 6
REGEX="${REGEX}|"
elif [ $endStartDiff -gt 1 ]; then
if [ $endStartDiff -eq 2 ]; then
REGEX="${REGEX}$startBegin[$(($startCut+1))]"
else
REGEX="${REGEX}$startBegin[$(($startCut+1))-$(($endCut-1))]"
fi
loop $diffChar 6
echo $REGEX
else
echo ${REGEX%?}
fi
else
if [ $4 -eq 0 ]; then
if [ $startCut -lt 5 ]; then
REGEX="${REGEX}$startBegin[$startCut-5][0-9]"
loop $diffChar 5
REGEX="${REGEX}|"
fi
if [ $endCut -gt 0 ]; then
REGEX="${REGEX}$endBegin[0-$endCut][0-9]"
loop $diffChar 5
REGEX="${REGEX}|"
fi
elif [ $endStartDiff -gt 1 ]; then
if [ $diffCharMinusOne -gt 0 ]; then
REGEX="${REGEX}$startBegin"
fi
if [ $endStartDiff -eq 2 ]; then
REGEX="${REGEX}[$(($startCut+1))][0-9]"
else
REGEX="${REGEX}[$(($startCut+1))-$(($endCut-1))][0-9]"
fi
loop $diffChar 5
echo $REGEX
else
echo ${REGEX%?}
fi
fi
fi
}
for a in {0..5}
do
if [ ${END_TIME:$a:1} -gt ${START_TIME:$a:1} ];then
DIFF_CHAR=$(($a+1))
break
fi
done
result=$(regexGen $DIFF_CHAR $START_TIME $END_TIME 1 | sed 's/\([0-9][0-9]\)/\1:/g')
echo $result