bash compute average return bad answer - bash

I have a script with:
#!/bin/bash
mark=10+10
nb=2
echo $mark
echo $(($mark/$nb))
I don't understand why the second echo return 15.

It's because the precendence of operators, and because mark is not evaluated itself (in a math-sense of evaulation).
echo $(($mark/$nb)) is first replace to $((10+10/2)) which is then evaluated to 10+5 which is 5.
There are numerous solutions to the problem, e.g:
echo $((mark/nb)
echo $((($mark)/$nb))

Try this instead:
#!/bin/bash
mark=$((10+10))
nb=2
echo $mark
echo $(($mark/$nb))
The reason is that when assigning 10+10 no result is calculated. Instead 10+10/$nb is executed in the last line. And the result is 15 of course.

Related

Have unique values in each while loop iteration

What I try to achieve, is to define global variables in my script. These variables can be reused in a loop (preferably a while loop..) and with every iteration, the loop should get a new set a variables.
My script (so far):
PACKAGE_ASSET_ID=AUTO`date +%s`000001
TITLE_ASSET_ID=AUTO`date +%s`000002
MOVIE_ASSET_ID=AUTO`date +%s`000003
PREVIEW_ASSET_ID=AUTO`date +%s`000004
POSTER_ASSET_ID=AUTO`date +%s`000005
while read name; do
#DATE=`date +%s`
#PACKAGE_ASSET_ID="AUTO${DATE}000001"
#TITLE_ASSET_ID="AUTO${DATE}000002"
#MOVIE_ASSET_ID="AUTO${DATE}000003"
#PREVIEW_ASSET_ID="AUTO${DATE}000004"
#POSTER_ASSET_ID="AUTO${DATE}000005"
echo $PACKAGE_ASSET_ID
echo $TITLE_ASSET_ID
echo $MOVIE_ASSET_ID
echo $PREVIEW_ASSET_ID
echo $POSTER_ASSET_ID
done <names.txt
Within the file names.txt, there are 15 entries. For every entry, the while loop needs to process these sets of variables. Giving me something like
AUTO1521884581000001
AUTO1521884581000002
AUTO1521884581000003
AUTO1521884581000004
AUTO1521884581000005
AUTO1521884592000001
AUTO1521884592000002
AUTO1521884592000003
AUTO1521884592000004
AUTO1521884592000005
As you can see in the script, I tried putting it into the while loop and with different syntax but regretfully, without success. The results I get are always the same set of variables, for all the 15 entries.
Did you really expect that bash (even bash!) would need more than a second to read one line?? Try adding the nanoseconds.
while read name; do
DATE=$(date +%s%N)
PACKAGE_ASSET_ID="AUTO${DATE}000001"
TITLE_ASSET_ID="AUTO${DATE}000002"
MOVIE_ASSET_ID="AUTO${DATE}000003"
PREVIEW_ASSET_ID="AUTO${DATE}000004"
POSTER_ASSET_ID="AUTO${DATE}000005"
echo $PACKAGE_ASSET_ID
echo $TITLE_ASSET_ID
echo $MOVIE_ASSET_ID
echo $PREVIEW_ASSET_ID
echo $POSTER_ASSET_ID
done <names.txt

Bash multiple parameter substitution

I'm looking to set the value of a variable to one thing if it was already set or another if it was not. Here's an example of what I mean
export RESULT=${VALID:+Yes:-No}
Where the value of ${VALID:+Yes:-No} would be Yes if the variable was set or No if it was not.
One way I can do it now:
if [ -n "${VALID}" ]; then
export RESULT=Yes
else
export RESULT=No
fi
I could do it like this, but it would be nice to have a "one-liner".
Is it possible to do this in one line?
There's no way to do multiple parameter expansions within one variable assignement.
Without using an if statement, you can just use a couple of parameter expansions.
$ VALID="aaa"
$ RESULT="${VALID:+Yes}"; RESULT="${RESULT:-No}"; echo $RESULT
Yes
$ VALID=""
$ RESULT="${VALID:+Yes}"; RESULT="${RESULT:-No}"; echo $RESULT
No
It's not a single statement, but it's short enough that it fits on one line without the complexity of a subshell and if.
You can use an array with 2 elements (no yes) and based on variable's length decide which value to be returned:
# variable set case
>>> VALID=foo
>>> arr=(no yes) && echo "${arr[$((${#VALID} > 0))]}"
yes
# variable not set
>>> unset VALID
>>> arr=(no yes) && echo "${arr[$((${#VALID} > 0))]}"
no
The -v VAR printf option is a bash extension. Without it, you could use command substitution. (Variable names deliberately down-cased.)
printf -v result %.3s ${valid:+YES}NO
One line, requirement met:
export RESULT=$([ -n "$VALID" ] && echo Yes || echo No)

Iterating over variable name in bash script

I needed to run a script over a bunch of files, which paths were assigned to train1, train2, ... , train20, and I thought 'why not make it automatic with a bash script?'.
So I did something like:
train1=path/to/first/file
train2=path/to/second/file
...
train20=path/to/third/file
for i in {1..20}
do
python something.py train$i
done
which didn't work because train$i echoes train1's name, but not its value.
So I tried unsuccessfully things like $(train$i) or ${train$i} or ${!train$i}.
Does anyone know how to catch the correct value of these variables?
Use an array.
Bash does have variable indirection, so you can say
for varname in train{1..20}
do
python something.py "${!varname}"
done
The ! introduces the indirection, so "get the value of the variable named by the value of varname"
But use an array. You can make the definition very readable:
trains=(
path/to/first/file
path/to/second/file
...
path/to/third/file
)
Note that this array's first index is at position zero, so:
for ((i=0; i<${#trains[#]}; i++)); do
echo "train $i is ${trains[$i]}"
done
or
for idx in "${!trains[#]}"; do
echo "train $idx is ${trains[$idx]}"
done
You can use array:
train[1]=path/to/first/file
train[2]=path/to/second/file
...
train[20]=path/to/third/file
for i in {1..20}
do
python something.py ${train[$i]}
done
Or eval, but it awfull way:
train1=path/to/first/file
train2=path/to/second/file
...
train20=path/to/third/file
for i in {1..20}
do
eval "python something.py $train$i"
done

why is my loop variable not alive after 1 iteration?

I have this code
for p in "abra/cadabra reach/out"
do
r="$HOME/x/$p"
echo $r
done
But it only produces this:
/Users/terrencemonroebrannon/x/abra/cadabra reach/out
Not
/Users/terrencemonroebrannon/x/abra/cadabra /Users/terrencemonroebrannon/x/reach/out
as I would expect.
You have placed your two items in quotes, which bash interprets as a single token. That's what quotes are for. You would get the behavior you describe if you were to remove the quotes:
for p in abra/cadabra reach/out
do
r="$HOME/x/$p"
echo $r
done
Gives me:
/Users/lars/x/abra/cadabra
/Users/lars/x/reach/out
bash will see "abra/cadabra reach/out" as one token.
Quotes are not needed.
for p in abra/cadabra reach/out
do
r="$HOME/x/$p"
echo $r
done

BASH: Assign '&' to variable NOT as string

I wanted to conditionally run a command as a background or foreground process, so I wrote something like this:
test $some_var; bg_suffix=&
long_command $bg_suffix
it doesn't work because bg_suffix is always empty whether it's been assigned or not.
But
test $some_var; bg_suffix="&"
long_command $bg_suffix
doesn't work either because now bg_suffix is interpreted as a string.
Any ideas how to solve this problem? Thanks!
Here is how to do it without using a quote-breaking eval
inBackground () {
t=$1
shift
if $t; then
"$#"&
else
"$#"
fi
}
This lets you do something like:
inBackground false echo '$$'
inBackground true sleep 4
This gets around the problem that all the eval-based solutions have: new and sometimes impossible quoting rules. For example, try to pass the '$$' through eval. Because true and false are not significant to the parser they can be in variables and things will still work.
Of course, if you wanted shell metachars to work (say, you redirect i/o) then eval is better, or you need to define a procedure for the command, and if you define a procedure, you problem is solved:
complicated_command () {
sleep 3
echo replace this with something complex
}
do_background=true
$do_background && (complicated_command&) || complicated_command
How about:
if [[ ${somevar} ]] ; then
long_command &
else
long_command
fi
or, if it is a long command you don't want to have to enter twice:
long_command=insert your big honking command here
if [[ ${somevar} ]] ; then
${long_command} &
else
${long_command}
fi
Just as an aside, I hope you're aware that the command sequence:
test ${condition}; x=2
will set x to 2 regardless of the test results. You may have meant to write:
test ${condition} && x=2
did you try
eval (long_command $bg_suffix)
using bg_suffix="&"
I don't know why I am not able comment, but anyway
test $some_var; bg_suffix="&"
would cause bg_suffix to be set regardless of the result of test.

Resources