trying to make a bash script that sets a value for use later in the script, depending on what I send when I run it;
i.e. ./script.sh 24
code:
### Setting values depending on input
#
# If value is between 7 and 13
if [[ $1 -le 13 || $1 -ge 7 ]]; then
#set value to
VALUE=7
# else if value is between 14 and 29
elif [[ $1 -le 29 || $1 -ge 14 ]]; then
#else set value to
VALUE=14
# else if value is larger than 30
elif [[ $1 -le 30 ]]; then
#Set value to VALUE=30
#else
# echo nope
fi
# This is just for showing what's going on
echo "input: $1"; echo "value: $VALUE"
# Do other stuff here...
But I can't get it to work properly, it only sets "value" to 7, no matter what i send on script start.
Does bash read values in any special order? When I run it with ./script.sh 24 I gives "value=7" but it (in my mind) should be "value=14"
In the first comparison:
$1 -le 13 || $1 -ge 7
You are checking if a value is below 13 or above 7. All natural numbers matches this condition, like: 1 is below 13, 100 is avobe 7, and 10 is below 13 and avobe 7. So you always enter in the first branch.
Maybe you are looking for numbers above 7 AND below 13:
$1 -le 13 && $1 -ge 7
Related
I have the following variable with a list of numbers
vlist="1 13 20 21 22 24 25 28 240 131 133 136 138 224"
In the next loop I want to input a number between 1 - 250 except the numbers in vlist
while :; do
echo -en 'Number : ' ; read -n3 cvip ; echo
[[ $cvip =~ ^[0-9]+$ ]] || { echo -e '\nSorry input a number between 1 and 239 except '$vlist'\n' ; continue; }
cvip=$(expr ${cvip} + 0)
if ((cvip >= 1 && cvip <= 250)); then break ; else echo -e '\nNumber out of Range, input a number between 1 and 239 except '$vlist'\n' ; fi
done
Ηow can I enter the list exception inside the if statement range
If using bash, one approach is to store the bad numbers as keys in an associative array and see if that particular key exists when validating a number:
#!/usr/bin/env bash
vlist="1 13 20 21 22 24 25 28 240 131 133 136 138 224"
declare -A nums
for num in $vlist; do
nums[$num]=1
done
while true; do
while read -p "Number: " -r num; do
if [[ ! $num =~ ^[[:digit:]]+$ ]]; then
echo "Input an integer."
elif [[ ${nums[$num]:-0} -eq 1 ]]; then
echo "Input a number between 1 and 250 except '$vlist'"
elif [[ $num -lt 1 || $num -gt 250 ]]; then
echo "Input an integer between 1 and 250"
else
break
fi
done
printf "You successfully inputted %d\n" "$num"
done
The important bit is ${nums[$num]:-0}, which expands to the value of that element of the associative array if that key exists and is not null, or 0 otherwise. As noted by Glenn in a comment, in bash 4.3 or newer, [[ -v nums[$num] ]] works too for testing to see if a given key exists, so [[ ${nums[$num]:-0} -eq 1 ]] in the above could be replaced with it.
I have $MM. It can be:
01-12
But not 1-12 (i.e. every single digit will have a 0 in front, 1=01, 2=02 etc.)
I need to write a test that ensures that MM is valid, and fail if not. I have tried:
MM=09
if [[ "$MM" -le 0 ]] && [[ "$MM" -ge 13 ]]; then
echo "MM is not between 01 and 12"
fi
errors:
[[: 09: value too great for base (error token is "09")
I have also tried various combinations:
MM=13
if [ $MM -le 0 ] && [ $MM -ge 13 ]; then
echo "MM is not between 01 and 12"
fi
shows nothing when MM is 13 (to be expected)
MM=13
if [ $MM <= 0 ] && [ $MM => 13 ]; then
echo "MM is not between 01 and 12"
fi
errors
=: No such file or directory
I know I am doing something fundamentally wrong here, I'm just not sure what it is. I also need this logic to apply similar checks for:
DD needs to be between 01-31
hh needs to be between 00-23
mm needs to be between 00-59
thanks!
First of all leading 0 makes shell interpret it as octal number and 09 is invalid octal so you will get: 09: value too great for base error.
You can use use BASH regex for this validation:
mm='09'
[[ $mm =~ ^(0[1-9]|1[0-2])$ ]] && echo "valid" || echo "invalid"
valid
mm='9'
[[ $mm =~ ^(0[1-9]|1[0-2])$ ]] && echo "valid" || echo "invalid"
invalid
mm='13'
[[ $mm =~ ^(0[1-9]|1[0-2])$ ]] && echo "valid" || echo "invalid"
invalid
mm='12'
[[ $mm =~ ^(0[1-9]|1[0-2])$ ]] && echo "valid" || echo "invalid"
valid
Just use arithmetic comparison, specifying the radix:
if (( 10#$mm < 1 || 10#$mm > 12 )); then
echo "mm is not between 1 and 12"
fi
or, similarly,
if (( 10#$mm >= 1 && 10#$mm <= 12 )); then
echo "mm is between 1 and 12"
fi
You specify the radix by using radix# in front of the number/variable expansion. Here we need radix 10, hence the 10# in front of the expansion $mm. See Shell Arithmetic in the reference manual (near the end of the section).
The other answer (with regexes) is good, but doesn't carry the proper semantic, and can be really awkward with more complicated ranges.
In case you want sh compatibility, the case statement is handy.
case $MM in ? | ???* | *[!0-9]* | 1[3-9] | [2-9]? | 00) echo Invalid;; esac
This triggers on, in turn, too short, too long, non-numeric, too large smaller than 20, other too large numbers, and all zeros.
So, to run my script I use a parameter like this:
./script 789
The parameter is to change the permissions of a file or a directory, and I'd like to check if every digit is between 0 and 7, so what I tried is the next thing:
if [[ ${1:0:1} -ge 0 && ${1:0:1} -le 7 ]] && [[ ${1:1:1} -ge 0 && ${1:1:1} -le 7]] && [[ ${1:2:1} -ge 0 && ${1:2:1} -le 7]]
then {go ahead with the code}
else
echo "Error: each digit in the parameter must be between 0 and 7"
fi
If it's true, then go ahead with the script, else show an error message, but it doesn't seem to work.
You want to match the parameter with the regex [0-7][0-7][0-7], or [0-7]{3}. In bash, you can do:
[[ "$1" =~ [0-7]{3} ]] || { echo 'invalid parameter' >&2; exit 1; }
Or:
echo "$1" | grep -Eq '[0-7]{3}' || { echo err message >&2; exit 1; }
this could be another way
#!/bin/bash
#Stored input parameter 1 in a variable
perm="$1"
#Checking if inserted parameter is empty, in that case the script will show a help
if [ -z "${perm}" ];then
echo "Usage: $0 <permission>"
echo "I.e.: $0 777"
exit
else
#if the parameter is not empy, check if each digit is between 0-7
check=`echo $perm| grep [0-7][0-7][0-7]`
#if the result of command is empty that means that the input parameter contains at least one digit that's differs from 0-7
if [ -z "${check}" ];then
echo "Error: each digit in the parameter must be between 0 and 7"
fi
fi
that's the output
[shell] ➤ ./test9.ksh 999
Error: each digit in the parameter must be between 0 and 7
[shell] ➤ ./test9.ksh 789
Error: each digit in the parameter must be between 0 and 7
[shell] ➤ ./test9.ksh 779
Error: each digit in the parameter must be between 0 and 7
[shell] ➤ ./test9.ksh 777
I would like to download file with the format cars000.txt, cars003.txt,cars006.txt, till cars105.txt...interval of 3 as you can see
I use the following code in ksh, but after downloading cars012.txt, it fails, it begins to download cars13.txt,...and I don't wish it. What does it fails in the code?
FHR=000
while [ $FHR -le 105 ]
do
file=cars${FHR}.txt
wget http://${dir_target}/${file}
(( FHR = $FHR + 03 ))
echo $FHR
if [[ $FHR -le 10 ]]; then FHR="00"$FHR
else FHR="0"$FHR
fi
done
You should decide: is FHR a string, a decimal or an octal.
You are mixing them currently.
Try the next improvement:
FHR=0
while [ ${FHR} -le 105 ]; do
file=cars${FHR}.txt
(( FHR = FHR + 3 ))
echo Without leading zero: FHR=${FHR}
if [[ $FHR -le 10 ]]; then
echo "FHR=00${FHR}"
else
echo "FHR=0${FHR}"
fi
sleep 1
done
(The next improvement might be using printf or awk and no zero for 102/105)
I used a while read loop in shell script to count and number line by line my file.txt. Now I want to give, inside the loop, the exact number of lines, like if I'm command wc -l. Below is my script.
#!/bin/bash
let count=0
while read cdat ctim clat clon
do
h=${ctim:0:2}; # substring hours from ctim
m=${ctim:3:2};
s=${ctim:6:2};
# echo $j
if [[ $h>=11 ]]; then
if [[ $h<=18 ]] && [[ $s<=00 ]]; then
if [[ $m != 01 ]]; then # spaces around "!=" is necessary
echo "$count $LINE" $cdat $ctim $clat $clon
let count=$count+1
fi
fi
fi
done < cloud.txt
exit
And output contains lines like:
0 2014/04/00 14:44:00 26.12 -23.22
1 2014/11/21 16:05:00 19.56 -05.30
2 2014/01/31 13:55:00 02.00 31.10
3 2014/04/00 14:20:00 17.42 12.14
4 2014/07/25 15:30:00 35.25 05.90
5 2014/05/15 12:07:00 23.95 07.11
6 2014/07/29 17:34:00 44.00 17.43
7 2014/03/20 18:00:00 -11.12 -22.05
8 2014/09/21 12:00:00 06.44 41.55
My question is how to find that the output contains 9 lines?
This does not answer your specific question
if [[ $h>=11 ]]; then
if [[ $h<=18 ]] && [[ $s<=00 ]]; then
All of those tests always return true.
The test, [ and [[ commands act differently based on the number of arguments they see.
All those tests have 1 single argument. In that case, if it's a non-empty string, you have a success return code.
Crucial crucial crucial to put whitespace around the operators.
if [[ $h >= 11 ]]; then
if [[ $h <= 18 ]] && [[ $s <= 00 ]]; then
Question for you: what do you expect this test to do? [[ $s <= 00 ]]
Be aware that these are all lexical comparisions. You probably want this instead:
# if hour is between 11 and 18 inclusive
if (( 10#$h >= 11 && 10#$h <= 18 )); then
You already know this value with $count. Since you're counting from 0, the number you want is $count+1.
If I add the command "wc -l" at the end of my loop, I can get exactly what I wanted, means that number of lines (9 lines). But I want to know if there is a way to get it exactly inside the loop, maybe using the same command "wc -l" or not.
#!/bin/bash
let count=0
while read cdat ctim clat clon
do
h=${ctim:0:2}; # substring hours from ctim
m=${ctim:3:2};
s=${ctim:6:2};
if [[ $h>=11 && $h<=17 ]] || [[ $ctim == "18:00:00" ]]; then
echo "$count $LINE" $cdat $ctim $clat $clon
let count=$count+1
fi
done < cloud.txt | wc -l
exit
The result is exactly: 9
But now how to do it inside the loop?