im figuring out how to get this working..
Im not very experienced with Bin Bash,
but I have this script:
The variables.cfg contains:
pvp="0400 1200 2000"
#!/bin/sh
CONFIG_FILE="/home/nacion/variables.cfg"
source $CONFIG_FILE
#Arreglar tiempo
ATM (){
if [ $((1+2)) -gt 24 ];
then
$a=$1 + $2
$b=$a-2400
echo $b
fi
}
declare -a VcHorarios=($pvp)
for HoraAR in "${VcHorarios[#]}"
do
I dont know why is not passing the param
ES=$(ATM $HoraAR 500)
HorarioSv="&b&lMX $((HoraAR-300)) - AR $HoraAR H - ES $ES H"
done
echo $HorarioSv
The result:
[root#anda ~]# /home/nacion/vcprueba.sh
&b&lMX 1700 - AR 2000 H - ES H
[root#ns500347 ~]#
Thanks in advance for your help
You can drop the $ from variable names inside $((...)). $1 and $2 are not variables; they are positional parameters.
if [ $(($1 + $2)) -gt 24 ];
Once you manage to enter the body of the if statement, the correct commands are
a=$(( $1 + $2 ))
b=$(( a - 2400 )) # or $(( $a - 2400 )), if you prefer
Related
I need to accept input from user (i.e. 'read').
This input can be either a single positive number or a range of numbers (in the form X-Y ).
I then need to validate this input and perform an iterative loop through the range of numbers (or just once in the case of only X).
examples:
1) User supplies: "8" or "8-"
Loop runs only a single time supplying the number 8
2) User supplies: "13-22"
Loop runs 11 times (13 through 22) referencing the number 13.
3) User supplies: "22-13"
Probably should behave like #2 above...but I'm open to other clean ways to validate the input.
I have worked on the following so far, which isn't very clean, complete, or even 100% correct for what I was asking - but it shows the effort and idea I'm going for:
echo "line?"; read -r deleteline
case "$deleteline" in
''|*[!0-9\-]*) echo "not a number";;
[1-9]|[1-9][0-9]*);;
esac
deleteline_lb=$(echo $deleteline|awk -F "-" '{print $1}')
deleteline_ub=$(echo $deleteline|awk -F "-" '{print $2}')
if [ ! $deleteline_lb = "" ] && [ ! "$deleteline_ub" = "" ]; then
delete_line_count=1
delete_line_count=$(expr $deleteline_ub - $deleteline_lb)
if [ $delete_line_count -le 0 ]; then
delete_line_count=1
fi
fi
i=1; while [ $i -le $delete_line_count ]; do
echo $deleteline_lb $i
i=$(($i + 1))
done
This needs to run in sh, things like seq are not supported - so stick with posix compliant methods...
To clarify I am looking to do the following (pseudo-code):
1) accept input from user
2) validate if input is in the form "#" or "#-#" (range).
3) Execute chosen (arbitrary) code path based on proper/improper input.
4) If single # is given then store that to variable to perform future operations against.
5) If range is given, store both numbers in variable to be able to perform the operation against the lower # up to the higher number. More specifically it would be "(higher #) - (lower #) + 1". So if range were 12-17 then we need to perform operation against 12, 6x. (17 - 12 + 1). IOW, 12-17 inclusive.
6) A way to easily denote if data set is range vs single number is also desired so that code path to each can be easily branched.
thanks for helping!
UPDATE:
Using my basic code I reworked it (with a bit of input from a friend), and basically came up with this:
while true;do
printf "\\delete lines? [e=Exit] ";read -r deleteline
case "$deleteline" in
[Ee]) break;;
*)
echo "$deleteline" | egrep -q '^[[:digit:]-]*$'
if [ $? -ne 0 ]; then
printf "\\n input is not a number.\\n"
else
delete_range_start=`echo $deleteline|awk -F "-" '{print $1}'`
delete_range_end=`echo $deleteline|awk -F "-" '{print $2}'`
if [ $delete_range_end -lt $delete_range_start ]; then
printf "\\n upper range must be higher than lower range.\\n"
else
if [ "$delete_range_end" = "" ]; then
delete_range_end=$delete_range_start
elif [ $delete_range_end -gt $lineNumbers ]; then
printf "\\Setting range to last entry\\n"
fi
break
fi
fi
;;
esac
done
deleteline=$delete_range_start
deleteloop=`expr $delete_range_end - $delete_range_start + 1`
i=1
while [ $i -le $deleteloop ]; do
# Insert all processing code in here
i=`expr $i + 1`
done
If you have a posix compliant awk, try this:
echo "$userInput" | awk -F- '
($1+0==$1&&$2+0==$2){
for(i=$1;($1<$2?i<=$2:i>=$2);)
print ($1<$2?i++:i--);
next
}
$1+0==$1{
print $1;
next
}
$2+0==$2{
print $2;
next
}
($1+0!=$1&&$2+0!=$2){
exit 1
}'
The script check if the 2 fields (separated with -) are numbers. If so, it prints these numbers in an ascending or descending way depending if the first number is greater or lower than the second one.
If only one input, the script just prints it.
If none of the field are number, it exits with a non zero value.
This script could be the validation step of a shell script like this:
$ cat test.sh
#!/bin/sh
echo -n "range: "
read -r range
validated_input=$(echo "$range" | awk -F- '($1+0==$1&&$2+0==$2){for(i=$1;($1<$2?i<=$2:i>=$2);)print ($1<$2?i++:i--);next}$1+0==$1{print $1;next}$2+0==$2{print $2;next}($1+0!=$1&&$2+0!=$2){exit 1}')
if [ $? -ne 0 ]; then
echo "Incorrect range" >&2
fi
for i in $validated_input; do
echo "$i"
done
Examples:
$ ./test.sh
range: 10-6
10
9
8
7
6
$ ./test.sh
range: 8-
8
$ ./test.sh
range: hello
Incorrect range
I want to use modulo in a Bash script, but the result is always to low.
I need something like this: days = jdn mod p
this is what i have:
#!/bin/bash
if [ $# -ne 2 ]
then
echo "Fout, syntaxis: $0 maand(in cijfers) jaar"
exit 1
fi
a=$(echo "14 - $1"|bc)
y=$(echo "$2 + 4800 - $a"|bc)
m=$(echo "12 * $a - 3 + $1"|bc)
jdn=$(echo "scale=2;1 + (153 * $m +2)/5 + 365 * $y +${y}/4 - ${y}/100 - 32045"|bc)
jdn=$(echo "$jdn + 1"|bc|sed "s/...$//")
d=1
p=$(echo "29.530588853")
while [ "$d" != "32" ]
do
echo -n "$d"
days=$(echo "scale=2;${jdn} % ${p}"|bc)
fase=$(echo "scale=2;$p / $days"|bc)
fase1=$(echo "${fase}<7.382647213"|bc)
if [ $fase1 -eq 1 ]
then
echo -n "#"
elif [ $(echo "${fase}<14.76529443"|bc) -eq 1 ]
then
echo -n ")"
elif [ $(echo "$fase<22.14794164"|bc) -eq 1 ]
then
echo -n "0"
else
echo -n "("
fi
d=$(echo "$d + 1"|bc)
jdn=$(echo "$jdn +1"|bc)
done
jdn has as result 2455369, days should be 9.55 but the result is 0.054...
Arithmetic in Bash shell is done with integers only, so it is better to change the approach (or multiply the qantities by a large power of ten to make them be integers).
When you are doing math with integers in Bash, include the arithmetic in double parentheses, or to use a 'let' keyword.
((${jdn} % ${p}))
as described in these sources.
http://ubuntuforums.org/showthread.php?t=1970559
How to use mod operator in bash?
I've found a solution: without scale, it works fine. So this is my code:
days=$(echo "${jdn} % ${p}"|bc)
I only had to make sure that the modulo did'nt give a 0 back and than i could set scale in the division. It seemed to be more a math question at the end...
I want to take the absolute of a number by the following code in bash:
#!/bin/bash
echo "Enter the first file name: "
read first
echo "Enter the second file name: "
read second
s1=$(stat --format=%s "$first")
s2=$(stat -c '%s' "$second")
res= expr $s2 - $s1
if [ "$res" -lt 0 ]
then
res=$res \* -1
fi
echo $res
Now the problem I am facing is in the if statement, no matter what I changes it always goes in the if, I tried to put [[ ]] around the statement but nothing.
Here is the error:
./p6.sh: line 13: [: : integer expression expected
You might just take ${var#-}.
${var#Pattern} Remove from $var the shortest part of $Pattern that matches the front end of $var. tdlp
Example:
s2=5; s1=4
s3=$((s1-s2))
echo $s3
-1
echo ${s3#-}
1
$ s2=5 s1=4
$ echo $s2 $s1
5 4
$ res= expr $s2 - $s1
1
$ echo $res
What's actually happening on the fourth line is that res is being set to nothing and exported for the expr command. Thus, when you run [ "$res" -lt 0 ] res is expanding to nothing and you see the error.
You could just use an arithmetic expression:
$ (( res=s2-s1 ))
$ echo $res
1
Arithmetic context guarantees the result will be an integer, so even if all your terms are undefined to begin with, you will get an integer result (namely zero).
$ (( res = whoknows - whocares )); echo $res
0
Alternatively, you can tell the shell that res is an integer by declaring it as such:
$ declare -i res
$ res=s2-s1
The interesting thing here is that the right hand side of an assignment is treated in arithmetic context, so you don't need the $ for the expansions.
I know this thread is WAY old at this point, but I wanted to share a function I wrote that could help with this:
abs() {
[[ $[ $# ] -lt 0 ]] && echo "$[ ($#) * -1 ]" || echo "$[ $# ]"
}
This will take any mathematical/numeric expression as an argument and return the absolute value. For instance: abs -4 => 4 or abs 5-8 => 3
A workaround: try to eliminate the minus sign.
with sed
x=-12
x=$( sed "s/-//" <<< $x )
echo $x
12
Checking the first character with parameter expansion
x=-12
[[ ${x:0:1} = '-' ]] && x=${x:1} || :
echo $x
12
This syntax is a ternary opeartor. The colon ':' is the do-nothing instruction.
or substitute the '-' sign with nothing (again parameter expansion)
x=-12
echo ${x/-/}
12
Personally, scripting bash appears easier to me when I think string-first.
I translated this solution to bash. I like it more than the accepted string manipulation method or other conditionals because it keeps the abs() process inside the mathematical section
abs_x=$(( x * ((x>0) - (x<0)) ))
x=-3
abs_x= -3 * (0-1) = 3
x=4
abs_x= 4 * (1-0) = 4
For the purist, assuming bash and a relatively recent one (I tested on 4.2 and 5.1):
abs() {
declare -i _value
_value=$1
(( _value < 0 )) && _value=$(( _value * -1 ))
printf "%d\n" $_value
}
If you don't care about the math and only the result matters, you may use
echo $res | awk -F- '{print $NF}'
The simplest solution:
res="${res/#-}"
Deletes only one / occurrence if - is at the first # character.
I need to write a Bash script that source another script (config script) for hours. If the hour mentioned in config script matches the Linux past hour it needs to print the hour.
$ cat ConfHours.sh
#!/bin/bash --
Hours=(0 1 2 22 23)
$ cat Foo.sh
#!/bin/bash --
source /home/Geo/ConfHours.sh
arrayHours=( ${HOURS} )
for v in "${arrayHours[#]}"
do
HOUR=$(( $(date +%H) -1))
if [ "${HOUR}" == "v" ] ; then
HOUR = ${HOUR}
echo $HOUR
fi
done
When I run Foo.sh, I do not get anything. Could you please correct me where I am wrong?
Some errors:
source /home/Geo/ConfHours.sh
arrayHours=( ${HOURS} )
ConfHours defines a variable named Hours -- different variable
for v in "${arrayHours[#]}"
do
HOUR=$(date -d "1 hour ago" +%H)
You don't need to define this every time through the loop: put it before the for statement
if [ "${HOUR}" == "v" ] ; then
missing $ for the v variable
$HOUR will contain a leading 0 (due to %H)
a better test: if (( 10#$HOUR == 10#$v ))
HOUR = ${HOUR}
No spaces around the = allowed for variable assignment. Why are you trying to redefine the variable to itself?
echo $HOUR
fi
done
A more concise way to test an array contains a value is to take advantage of array string concatenation and pattern matching:
source ConfHours.sh
hour=$(date +%k) # leading space, not leading zero
if [[ " ${Hours[*]} " == *" ${hour# } "* ]]; then
echo "$hour"
fi
All spaces and quotes are required.
Don't use UPPER_CASE_VARS: here's why
I would like to do the following operation in my script:
1 - ((m - 20) / 34)
I would like to assign the result of this operation to another variable. I want my script use floating point math. For example, for m = 34:
results = 1 - ((34 - 20) / 34) == 0.588
You could use the bc calculator. It will do arbitrary precision math using decimals (not binary floating point) if you set increease scale from its default of 0:
$ m=34
$ bc <<< "scale = 10; 1 - (($m - 20) / 34)"
.5882352942
The -l option will load the standard math library and default the scale to 20:
$ bc -l <<< "1 - (($m - 20) / 34)"
.58823529411764705883
You can then use printf to format the output, if you so choose:
printf "%.3f\n" "$(bc -l ...)"
Bash does not do floating point math. You can use awk or bc to handle this. Here is an awk example:
$ m=34; awk -v m=$m 'BEGIN { print 1 - ((m - 20) / 34) }'
0.588235
To assign the output to a variable:
var=$(awk -v m=$m 'BEGIN { print 1 - ((m - 20) / 34) }')
Teach bash e.g. integer division with floating point results:
#!/bin/bash
div () # Arguments: dividend and divisor
{
if [ $2 -eq 0 ]; then echo division by 0; return 1; fi
local p=12 # precision
local c=${c:-0} # precision counter
local d=. # decimal separator
local r=$(($1/$2)); echo -n $r # result of division
local m=$(($r*$2))
[ $c -eq 0 ] && [ $m -ne $1 ] && echo -n $d
[ $1 -eq $m ] || [ $c -eq $p ] && echo && return
local e=$(($1-$m))
c=$(($c+1))
div $(($e*10)) $2
}
result=$(div 1080 633) # write to variable
echo $result
result=$(div 7 34)
echo $result
result=$(div 8 32)
echo $result
result=$(div 246891510 2)
echo $result
result=$(div 5000000 177)
echo $result
Output:
1.706161137440
0.205882352941
0.25
123445755
28248.587570621468
echo $a/$b|bc -l
gives the result.
Example:
read a b
echo $a/$b|bc -l
Enter a & b value as 10 3, you get 3.3333333333
If you want to store the value in another variable then use the code
read a b
c=`echo $a/$b|bc -l`
echo $c
It also gives the same result as above.
Try it...
I know this is an old thread, but this seemed like a fun project to tackle without using bc or invoking recursion. I'm sure it can be improved, but this maxed out my skill.
numerator=5
denominator=7 # - 0 -> returns "undef"
decimal_places=4 # - 0 -> same as echo $(( $numerator / $denominator ))
_result_sign=""
let _dp_exp=10**decimal_places
if [ $denominator -eq 0 ]; then _div_result_int_large=0; else let _div_result_int_large=$((numerator * _dp_exp / denominator)); fi
if [ $_div_result_int_large -lt 0 ]; then let _div_result_int_large=$(( _div_result_int_large * -1 )); _result_sign="-"; fi
let _div_result_int=$((_div_result_int_large / _dp_exp))
let _div_result_mant=$((_div_result_int_large - _div_result_int * _dp_exp))
let _dp_lzeros=$((decimal_places - ${#_div_result_mant}))
printf -v _div_result_mant_padded "%.${_dp_lzeros}d$_div_result_mant"
div_result="$_result_sign$_div_result_int"
if [ $decimal_places -gt 0 ]; then div_result="$_result_sign$_div_result_int.$_div_result_mant_padded"; fi
if [ $denominator -eq 0 ]; then div_result="undef"; fi
echo $div_result
Example output:
numerator=5
denominator=7
decimal_places=5
-> 0.71428
numerator=250
denominator=13
decimal_places=0
-> 19
numerator=-5
denominator=6
decimal_places=2
-> -0.83
numerator=3
denominator=0 # - uh-oh
decimal_places=2 # - can be anything, in this case
-> undef
Use this script open this file with favorite editor like:
$ sudo vim /usr/bin/div
Then paste this code:
#!/bin/bash
# Author: Danial Rikhteh Garan (danial.rikhtehgaran#gmail.com)
if [[ -z "$1" ]] || [[ -z "$2" ]]; then
echo "Please input two number"
echo "for 100/50 use: div 10 50"
exit 1;
fi
div=$(echo "$1/$2" | bc -l);
echo 0$div | sed 's/[0]*$//g'
Now chmod it to 755:
$ sudo chmod 755 /usr/bin/div
Now use it:
$ div 5 100
0.05
In your script you can use this:
var=$(div 5 100);
echo "$var"