Have some function with loop:
runcfg () {
o=0
while (( o<3 )); do
echo $o
(( o++ ))
done
}
It calls from another script manager.sh by:
...
-C|--runconfig)
source $APP_BASEDIR/app-install/bin/config_functions; runcfg;
shift;;
...
Let's run it:
$ bash -x manager.sh -run
And look at output:
...
+ runcfg
+ o=0
+ (( o<3 ))
+ echo 0
0
+ (( o++ ))
That's all lines... Nothing else happens.
But! If make little chage and add && after arithmetic call:
runcfg () {
o=0
while (( o<3 )); do
echo $o
(( o++ )) && echo ++OK || echo ++ER
done
}
It works!
...
+ runcfg
+ o=0
+ (( o<3 ))
+ echo 0
0
+ (( o++ ))
+ echo ++ER
++ER
+ (( o<3 ))
+ echo 1
1
+ (( o++ ))
+ echo ++OK
++OK
+ (( o<3 ))
+ echo 2
2
+ (( o++ ))
+ echo ++OK
++OK
+ (( o<3 ))
+ shift
...
What is this?
Your script is configured to exit on error (note that it prints ++ER the first time), and ((o++)) == ((0)), which returns 1 (an error). Options:
Use let o++ instead.
Ignore the error (((o++))||:)
Use a for loop (for ((o = 0; o < 3; o++)))
Turn off exit-on-error (set +e).
Related
I'd like to know how to print Fibonacci series using recursion in bash with only 1 variable.
From what I've done:
fib()
{
i=$1
if (( $i <= 1 ))
then echo 0
elif (( $i == 2 ))
then echo 1
else
echo $(( $(fib $(($i - 1)) ) + $(fib $(($i - 2)) ) ))
fi
}
echo $(fib $1)
I get the correct output of the final iteration, for example if I enter 10 I will get 34, but I'd like to print the whole sequence of numbers, i.e. all the iterations as well. How can I achieve that?
Another way I tried was by:
#!/bin/bash
arr[0]=0
arr[1]=1
for (( i=0; i<=10; i++ ))
do
echo -n "${arr[0]} "
arr[0]=$((${arr[0]} + ${arr[1]} ))
arr[1]=$((${arr[0]} - ${arr[1]} ))
done
echo ""
But obviously here I've used a for loop, but I don't want to use another variable.
Just for (my kind of) fun, this code prints the Fibonacci numbers from the 0th to the 92nd (as defined in Fibonacci number - Wikipedia) with a recursive function that uses no variables:
#! /bin/bash
function fib
{
echo ${3-0}
(($1 > 0)) && fib $(($1-1)) ${3-0} $((${2-1}+${3-0}))
}
fib 92
Some may claim that using the positional parameters ($1, $2, $3) for this is cheating, but then other solutions could be said to be using two variables ($i and $1).
The code takes under 0.01 seconds to run on my (oldish) Linux machine.
The code should work with numbers up to 92 with Bash version 3 or later on any platform. See Bash Number Limit?. Numbers higher than 93 will cause to code to produce garbage results due to arithmetic overflow.
Variables in bash are global by default. You need to make i local explicitly.
fib () {
local i
i=$1
if (( i <= 1 )); then
echo $i
else
echo $(( $(fib $((i-1)) ) + $(fib $((i - 2)) ) ))
fi
}
(Also, your base cases are a little off if you are starting with 0, and 2 need not be a base case; fib 2 can be derived from the base cases fib 0 and fib 1.)
If you want to print each fibonacci value from 1 to $n, I suggest:
fib_r() {
local i=$1
if (( i < 0 )); then
echo "Error: negative numbers not allowed" >&2
exit 1
elif (( i <= 1 )); then
echo $i
else
echo $(( $($FUNCNAME $((i - 1)) ) + $($FUNCNAME $((i - 2)) ) ))
fi
}
fib() {
local i
for (( i = 1; i <= $1; i++ )); do
fib_r $i
done
}
fib 10
outputs
0
1
1
2
3
5
8
13
21
34
It's still one variable, albeit one per function.
I use the bash variable $FUNCNAME in the recursive function so you don't have to hardcode the function name within itself. I got bit by not updating that line when I renamed the function.
Of course your performance will greatly improve if you cache the results: "fib 16" takes, on my VM, about 3.5 sec without caching and about 0.03 sec with caching.
fib_r() {
local i=$1
if (( i < 0 )); then
echo "Error: negative numbers not allowed" >&2
exit 1
elif [[ -n ${fib_cache[i]} ]]; then
echo "${fib_cache[i]}"
elif (( i <= 1 )); then
echo $i
else
echo $(( $( $FUNCNAME $((i - 1)) ) + $( $FUNCNAME $((i - 2)) ) ))
fi
}
fib_cache=()
fib() {
local i
for ((i=1; i<=$1; i++)); do
fib_cache[i]=$(fib_r $i)
echo "${fib_cache[i]}"
done
}
I am writing a bash script that will count occurences of [], (), {}, '', "", `` and // so that i can debug other scripts for missing one of them.
Here is the code:
a=0; b=0; cc=0; d=0; e=0; f=0; g=0; h=0; i=0; j=0 # set default values
squote=`echo -e "\x27"`
dquote=`echo -e "\x22"`
while IFS= read -r -n1 c; do
[ "$c" == "[" ] && (( a++ ))
[ "$c" == "]" ] && (( b++ ))
[ "$c" == "(" ] && (( cc++ )) # i imported this line from another online script, is there a reason why the author avoids (( c++ )) ?
[ "$c" == ")" ] && (( d++ ))
[ "$c" == "{" ] && (( e++ ))
[ "$c" == "}" ] && (( f++ ))
[ "$c" == "\x27" ] && (( g++ )) # single quote
[ "$c" == "$dquote" ] && (( h++ )) # double quote
[ "$c" == '`' ] && (( i++ )) # back tick
[ "$c" == '/' ] && (( j++ )) # forward slash
done < "$1"
echo '[]'="$a,$b"
echo '()'="$cc,$d"
echo '{}'="$e,$f"
echo "$squote" ="$g"
echo "$dquote" ="$h"
echo '``'="$i"
echo '/'="$j" # when used this way: /hello/
Questions:
1) why the cc++ instead of c++
2) why some results don't come out right at all.
3) How to annotate the special characters the right way.
Thank you in advance for your help.
some simplifications and functional script
#!/bin/bash
a=0; b=0; c=0; d=0; e=0; f=0; g=0; h=0; i=0; j=0
while read -r -n1 char; do
case "$char" in
"[" ) (( a++ )) ;;
"]" ) (( b++ )) ;;
"(" ) (( c++ )) ;;
")" ) (( d++ )) ;;
"{" ) (( e++ )) ;;
"}" ) (( f++ )) ;;
"'" ) (( g++ )) ;;
'"' ) (( h++ )) ;;
'`' ) (( i++ )) ;;
'/' ) (( j++ )) ;;
esac
done <<< "[]{}()/"$'\x60'$'\x22'$'\x27' # $'\x60' == backtick
echo '[]'="$a,$b"
echo '()'="$c,$d"
echo '{}'="$e,$f"
echo "'" ="$g"
echo '"' ="$h"
echo '`'="$i"
echo '/'="$j"
output
[]=1,1
()=1,1
{}=1,1
' =1
" =1
`=1
/=1
nota:
prefer: dquote=$( echo -e "\x22" ) # and not ``
better: dquote=$'\x22' # or \042 octal
simpler: dquote='"'
to check script and learn, I suggest to install shellcheck or use https://www.shellcheck.net/
initially:
[ "$char" == "[" ] && (( a++ ))
[ "$char" == "]" ] && (( b++ ))
[ "$char" == "(" ] && (( c++ ))
[ "$char" == ")" ] && (( d++ ))
[ "$char" == "{" ] && (( e++ ))
[ "$char" == "}" ] && (( f++ ))
[ "$char" == "'" ] && (( g++ ))
[ "$char" == '"' ] && (( h++ ))
[ "$char" == '`' ] && (( i++ ))
[ "$char" == '/' ] && (( j++ ))
but unnecessary tests are systematically performed.
we can also use:
if [[ $char == "[" ]]; then (( a++ ))
elif [[ $char == "]" ]]; then (( b++ ))
...
but performances with 10,000 iterations:
if > if elif > case
6720 ms > 2895 ms > 2073 ms
I have to a perform logic like this.
I have a array.
expression for this in shell
[(first no + arrlen) - ( index +1 ) - ge 10 ]
I have code this like this but it's not working
#!/bin/bash
array=(4 5 6 7 8 9)
for i in ${array[#]}
do
echo $i
done
echo "${#array[#]}"
l=${#array[#]}
count=0
for (( i=0; i < ${#array[#]}; i++ ))
do
if [ ($(`expr $i + $l`) - $(`expr ${!array[#]} + 1`)) -ge 10 ]
then
count=`expr $count + 1`
else
echo
fi
done
Your code could look like this:
#!/bin/bash
array=(4 5 6 7 8 9)
for i in "${array[#]}"; do
echo "$i"
done
length=${#array[#]}
first=${array[0]}
count=0
for (( i=0; i < length; i++ )); do
if (( (first + length) - (i + 1) >= 10 )); then
((count++))
else
echo "something"
fi
done
Don't use expr, use (( )) for arithmetic expressions
Quote expansions: "$i", "${array[#]}", ...
${!array[#]} expands to ALL indexes of your array, not the current index
I am new here and also a starter on programming so please handel me with care .
Filename = Datafile.tmp
I get a log file with 1 line of data like this (comma seperated)
24092014,17:21,357.7,5.1,49.96,228.6,7.9,1757.5,3378200.0,28.2,20680.0,846.0,1800.0,2
Maby i must explain shomething. I have a script writing in BASH that read out the data from the inverter by inside routine. I want to change that script so it read data out of the data file like above. The script is verry long but a place only the reading part of it.
So i think i can del all the reading port routines and just focus on the
convert data from array into readable data in my case from file.
and at the bottum storing variables to working file
#Ask data from inverter
get_data() {
printf $GetDataCMD > $PORT
dd if=$PORT of=$OUT bs=1 count=$dataLengthTotal &
PID=$!
sleep 3
if [ -d /proc/$PID ]
then
rcvd="no"
kill $PID
else
rcvd="yes"
fi
}
#Put received data in array and check for errors
receive_data() {
(( dataLengthTotal = $ANS_OFFSET+$dataLengthOutput+1 ))
retry_counter=$RETRY
while (( retry_counter )); do
get_data < $PORT 2>/dev/null
if [ $rcvd = "yes" ]; then
data=(`od -b -v $OUT | sed -e 's/^[0-9]*//g' | sed -e 's/[ ]\([0-9]\)/ 0\1/g'`)
if [ ${#data[#]} -ne $dataLengthTotal ]; then
rcvd="nochk"
echo -e `date -R`": Wrong amount of data received\r" >> $LOG
init_comport
(( retry_counter -= 1 ))
else
# Check CRC
sumstr="${data[#]:$ANS_OFFSET:$dataLengthOutput}"
let sum=${sumstr// /+}
if ((sum%256 == data[9+$dataLengthOutput])); then
rcvd="chk"
retry_counter=0
else
rcvd="nochk"
echo -e `date -R`": Checksum error in received data\r" >> $LOG
init_comport
(( retry_counter -= 1 ))
fi
fi
else
init_comport
(( retry_counter -= 1 ))
fi
done
}
#convert data from array into readable data
get_current_data() {
dataLengthOutput=30
GetDataCMD=$GetCurrentDataCMD
receive_data
if [ $rcvd = "chk" ]; then
#errorbits
(( errbits = data[9+7] * 256 + data[9+6] ))
#Voltage Solarpanels (Usol)
(( Usol = data[9+9] * 256 + data[9+8] ))
#Current Solarpanels (Isol)
(( Isol = data[9+11] * 256 + data[9+10] ))
#AC Frequency (Fnet)
(( Fnet = data[9+13] * 256 + data[9+12] ))
#AC Voltage (Unet)
(( Unet = data[9+15] * 256 + data[9+14] ))
#AC Current (Inet)
(( Inet = data[9+17] * 256 + data[9+16] ))
#AC Power (Pnet)
(( Pnet = data[9+19] * 256 + data[9+18] ))
#Total delivered (Etot)
(( Etot = data[9+22] * 65536 + data[9+21] * 256 + data[9+20] + WtotOffset ))
#Temp Inverter (Tinv)
(( Tinv = data[9+23] ))
#Total runtime (Htot)
(( Htot = data[9+26] * 65536 + data[9+25] * 256 + data[9+24] + HtotOffset ))
#Total CO2 (Ctot)
(( Ctot = Etot * $CO2perc / 100 ))
fi
}
#check, convert and write error messages
convert_errorbits() {
ErrorStr="Systeem_OK"
if (( errbits )); then
if (( errbits & 0x01 )); then
ErrorStr="Usolar too high (`print_float $Usol 1` V). ${ErrorStr}"
fi
if (( errbits & 0x02 )); then
ErrorStr="Usolar too low (`print_float $Usol 1` V). ${ErrorStr}"
fi
if (( errbits & 0x04 )); then
ErrorStr="No mains detected. ${ErrorStr}"
fi
if (( errbits & 0x08 )); then
ErrorStr="Uac too high (${Unet} V). ${ErrorStr}"
fi
if (( errbits & 0x10 )); then
ErrorStr="Uac too low (${Unet} V). ${ErrorStr}"
fi
if (( errbits & 0x20 )); then
ErrorStr="FreqAC too high (`print_float $Fnet 2` Hz). ${ErrorStr}"
fi
if (( errbits & 0x40 )); then
ErrorStr="FreqAC too low (`print_float $Fnet 2` Hz). ${ErrorStr}"
fi
if (( errbits & 0x80 )); then
ErrorStr="Temperature error (${Tinv}ÂșC). ${ErrorStr}"
fi
if (( errbits & 0x100 )); then
ErrorStr="Hardware error. ${ErrorStr}"
fi
if (( errbits & 0x200 )); then
ErrorStr="Starting. ${ErrorStr}"
fi
if (( errbits & 0x400 )); then
ErrorStr="Max output (${Pnet} W). ${ErrorStr}"
fi
if (( errbits & 0x800 )); then
ErrorStr="I max (`print_float $Isol 2` A). ${ErrorStr}"
fi
echo -e `date -R`": Error message: $ErrorStr\r" >> $LOG
fi
}
#Get current day output
get_current_day() {
dataLengthOutput=8
(( daynr = 0 ))
(( crc = $GetDailyDataCRC+$daynr ))
GetDataCMD=$GetDailyDataCMD1\\x`printf "%02X" $1 $daynr`$GetDailyDataCMD2\\x`printf "%02X" $1 $crc`
receive_data
if [ $rcvd = "chk" ]; then
# Profit [kWH]
(( Etoday = data[9+7] * 256 + data[9+6] ))
# Runtime [h:mm]
(( Htoday = data[9+5] * 5 ))
# CO2
(( CO2 = Etoday * $CO2perc / 100 ))
fi
}
# Main Program
main(){
#set time variables
MTIME=`date +%M`
HTIME=`date +%H`
# Configure serial port
init_comport
#Store file contains data needed for calculating totals etc
#Internally an array is used, values are:
#0=Active flag
#1=WtotAct
#2=HtotAct
#3=WtotLog
#4=HtotLog
#fill array
store=(`cat $STORE`)
get_current_data
if [ $rcvd = "chk" ]; then
if (( ! store[0] )); then
echo -e `date -R`": Waking up; Inverter started.\r" >> $LOG
store[0]=1
if (( USE_30DAYS )); then
get_last_30_days
fi
if [ ! -f $PVDIARYCSV ]
then
create_pvdiary
fi
if (( USE_EMAIL )) && (( USE_EMAwake )); then
email_send "Bericht van uw zonnepanelen : ontwaken." "I'm awake now !\n\nUw Mastervolt Omvormer\n"
fi
fi
# Remember 'old-values' for calculating PVDiary inputs
(( store[5] = store[1] ))
(( store[6] = 0 ))
# Put total values into array for calculating daily profit
store[1]=$Etot
store[2]=$Htot
convert_errorbits
get_current_day
create_actual_page
rrd_update
fill_pvdiary
fill_sql
pvoutput_live_update
fi
#Write message before sleep
if [ $rcvd = "no" ] && (( ! retry_counter )) && (( store[0] )); then
echo -e `date -R`": No reaction from Inverter; entering sleep\r" >> $LOG
create_offline_page
store[0]=0
fi
#Write 0-values in PVDiary-file
if [ $rcvd = "no" ] && (( ! retry_counter )) ; then
(( store[5] = store[1] ))
(( store[6] = 0 ))
fill_pvdiary
(( store[5] = store[1] ))
(( store[6] = 0 ))
fill_sql
(( store[5] = 0 ))
fi
#Run timebased scripts (internal cron)
if [ $HTIME = 23 ] && [ $MTIME = 00 ]; then
fill_csv
fi
if [ $MTIME = 00 ] && (( USE_RRD )); then
graph
fi
if [ $MTIME = 00 ] && (( store[0] )) && (( USE_EMAIL )) && (( USE_EMAhour )); then
email_send_actual "Bericht van uw zonnepanelen : productie tot nu toe."
fi
#storing variables to working file
echo ${store[0]} ${store[1]} ${store[2]} ${store[3]} ${store[4]} ${store[5]}${store[6]}> $STORE
}
#What to do ?
case $1 in
"help" ) help;;
"create" ) create;;
"draw" ) graph;;
* ) main;;
esac
$ awk -F, '{print "Today = " $1 "\nTime = " $2 "\nHertz = " $5}' file
Today = 24092014
Time = 17:21
Hertz = 49.96
The script takes only one argument (call it n). It then alternately adds and subtracts each digit of the numbers from 1 through n.
For example:
n = 12
1 - 2 + 3 - 4 + 5 - 6 + 7 - 8 + 9 - 1 + 0 - 1 + 1 - 1 + 2
output: 5
#!/bin/bash
number=$1
result=1
if (( number == 1 || number == 0 )); then
echo
echo number: $number
echo
else
for (( i = 1; i < number; i++ )); do
if (( i < 10 )); then
if (( i % 2 == 0 )); then
let "result = result + i + 1"
else
let "result = result - i - 1"
fi
else
for (( i = 10; i <= number; i++ )); do
if (( i < 100 && i >= 10)); then
let "result = result - i/10 + i%10"
else
let "result = result - i/100 + (i%100)/10 - (i/100)%10"
fi
done
fi
done
echo
echo result: $result
echo number: $number
echo
fi
It works fine for ints 0 to 9, but for n>9 it gives the wrong answer.
Please correct my script and explain to me why it's behaving like this.
PS I'm new to bash scripting, so I'd love to see another solution to this puzzle
PPS I'm familiar to java
EDIT
I fixed the code, and it works correct now
#!/bin/bash
number=$1
result=1
if (( number == 1 || number == 0 )); then
echo
echo number: $number
echo
else
for (( i = 1; i < number; i++ )); do
if (( i < 9 )); then
if (( i % 2 == 0 )); then
let "result = result + i + 1"
else
let "result = result - i - 1"
fi
fi
done
for (( i = 10; i <= number; i++ )); do
if (( i < 100 )); then
let "result = result - i/10 + i%10"
else
if (( i % 2 == 0 )); then
let "result = result - i/100 + (i%100)/10 - (i%100)%10"
else
let "result = result + i/100 - (i%100)/10 + (i%100)%10"
fi
fi
done
echo
echo result: $result
echo number: $number
echo
fi
Ok this should work apologies that I miss-read the question earlier
number=$1
MATSTR="";
for((i=1; i <= number; i++)); do
MATSTR="$MATSTR$i";
done;
echo $MATSTR | sed -e 's/\(.\)\(.\)/\1-\2+/g' | sed 's/+$//' | bc
Explanation:
For loop to create string concatenating all numbers from 1 to the number.
sed statement replaces the every two digits with 1st digit minus second digit plus
sed removes any trailing plus
bc calculates the statement
Here's your original solution, but with bug fixes (in comments). It still won't work for number > 99, of course.
#!/bin/bash
number=$1
result=0 # Don't start at 1!!
if (( number == 1 || number == 0 )); then
echo
echo number: $number
echo
else
for (( i = 1; i <= number; i++ )); do # <=, not <
if (( i < 10 )); then # < 10, not < 9
if (( i % 2 == 0 )); then
let "result = result - i" # subtraction! Also, the +1 was unnecessary
else
let "result = result + i" # addition!
fi
else
if (( i < 100 && i >= 10)); then
let result=result-i/10+i%10
else
let "result=result-i/100+(i%100)/10-(i/100)%10"
fi
fi
done
echo
echo result: $result
echo number: $number
echo
fi
After misreading the question, (or the question being unclear), the answer took only minor adjustments. The sting solution is probably the simplest approach using modulo to togget +/-:
#!/bin/bash
declare -i num=$1
declare -i res=0
str=""
[ "$num" -gt 1 ] || {
printf "\n Error: invalid input. usage: %s int (greater than 1)\n\n" "${0//*\//}"
exit 1
}
for ((i = 1; i <= $num; i++)); do # create the string of digits
str="${str}${i}"
done
printf "\n Calculations:\n\n"
for ((i = 0; i < ${#str}; i++)); do # walk down the string adding/subtracting each digit
if [ $((i % 2)) -eq 0 ]; then
((res+=${str:$i:1}))
printf " res + %3s = %3s\n" "${str:$i:1}" "$res"
else
((res-=${str:$i:1}))
printf " res - %3s = %3s\n" "${str:$i:1}" "$res"
fi
done
printf "\n Final Result: %s\n\n" "$res"
exit 0
output:
$ ./puzzle.sh 12
Calculations:
res + 1 = 1
res - 2 = -1
res + 3 = 2
res - 4 = -2
res + 5 = 3
res - 6 = -3
res + 7 = 4
res - 8 = -4
res + 9 = 5
res - 1 = 4
res + 0 = 4
res - 1 = 3
res + 1 = 4
res - 1 = 3
res + 2 = 5
Final Result: 5