Can a function be used in $(( func arg ? str1 : str2 ))? - bash

I'd like to use a statement like this:
var=$(( func arg ? str1 : str2 ))
but bash gives this syntax error message:
syntax error in expression (error token is "arg")
I've played with various forms but I can't figure out how to make it accept a function with argument. Any ideas?

echo $(( $(seq 1) + 1 ))
2
You need to use the same syntax as bash expects elsewhere. As far as the conditional ? iftrue : iffalse syntax, I don't think you can do that in bash. Instead, you can do something like:
echo $(( 1 + $(true && echo 1 || echo 0) ))
2

I think the correct answer is that there is no way to use the statement I asked about. The problem is that this conditional operator can only evaluate to an integer and not a string as I wanted to do.
jm666 answered the question in a comment to Steve's answer so I gave him an up vote.

Related

Process contents in array based on type in shellscript

I have an array that has three types of data in it, integer, integer/integer, and the string value.
I have shown a sample below.
myarr = (2301/2320,Team Lifeline, 2311, 7650/7670, 232)
I have the following algorithm that I want to come up with.
For index in myarr
if index contains data as number1/number2; then
create an array, "mynumbers" to hold all the numbers starting from number1 to number2
else if index is a string
add it in "mystrarr"
else
add it in "myintarr"
done
For the first case, if I have an enter in the myarr as 2301/2320,
then the mynumbers as shown in the pseudocode will have entries from {2301, 2302, ... , 2320}. I am not able to understand on how to parse the entry in myarr and identify that it has a / in the array.
For the second situation, I am also not sure on how to identify if the entry in the myarr and know it is a string. mystrarr should have {Team Lifeline}.
For the final case, the myintarr should have {2311, 232}.
Any help would be appreciated. I am very new to shell script.
Stack Overflow is not a coding service.... but I was bored so here you go...
#!/bin/bash
myarr=(2301/2320 'Team Lifeline' 2311 7650/7670 232)
for element in "${myarr[#]}"; do
if [[ $element =~ ^[0-9]+/[0-9]+$ ]]; then
range="{${element%/*}..${element##*/}}"
mynumbers=( $(eval "echo $range") )
elif [ $element -eq $element ] 2>> /dev/null; then
intarr+=( $element )
else
strarr+=( "$element" )
fi
done
echo "mynumbers = ${mynumbers[*]}"
echo "intarr = ${intarr[*]}"
echo "strarr = ${strarr[*]}"
A lot to unpack here for inexperienced. So ask questions where I didn't cover anything. Things to note:
All assignments there are no spaces around =.
Array assignments are of the format ( element1 element2 ... )
Appending to arrays with +=(...) format
Looping through array elements for element in "${myarr[#]}"
Note that the array generated by 7650/7670 will overwrite the array generated by 2301/2320. I assume you have some kind of plan for this array, so I didn't do anything to stop it from being overwritten.
More details
This line is validating the format for 111/222:
if [[ $element =~ ^[0-9]+/[0-9]+$ ]]; then
[[ x =~ x ]] performs a regex comparison and this regex essentially just means:
^ - beginning of the string
[0-9]+ - Atleast 1 number
/ - character literal
$ - end of string
These lines are expanding your beginning and ending numbers:
range="{${element%/*}..${element##*/}}"
mynumbers=( $(eval "echo $range") )
This is maybe more complicated than it needs to be as most people try to avoid eval in general for security reasons. I'm leveraging bash's brace expansion. If you run echo {5..9}, it will output 5 6 7 8 9. This does not trigger with variables, so I cheated and used eval.
This line is checking if we are dealing with an integer:
[ $element -eq $element ] 2>> /dev/null
This works by running an integer -eq (equals) comparison on the variable against itself. This will actually fail and throw an error message on anything but an integer. This is not the way it was designed to be used which is why we discard all the error messages (2>> /dev/null).
This is a nice succinct script, but is using some unconventional practices. A longer more verbose version may be better for a beginner.
You can use regular expressions to match elements that are nothing but digits, or digits/digits, and assume everything else is a string:
#!/bin/bash
myarr=(2301/2320 "Time Lifeline" 2311 7650/7670 232)
declare -a mynumbers mystrarr myintarr
for elem in "${myarr[#]}"; do
if [[ $elem =~ ^([0-9]+)/([0-9]+)$ ]]; then
mynumbers+=($(seq ${BASH_REMATCH[1]} ${BASH_REMATCH[2]}))
elif [[ $elem =~ ^[0-9]+$ ]]; then
myintarr+=($elem)
else
mystrarr+=("$elem")
fi
done
echo mynumbers is "${mynumbers[#]}"
echo myintarr is "${myintarr[#]}"
echo mystrarr is "${mystrarr[*]}"
Jason explained a lot in his (very similar; there's only so many obvious ways to do this) answer, so to expand on where ours are different:
We both use regular expressions to match the integer/integer case, but he then goes on to extract the two numbers using parameter expansion with pattern removal options, while mine captures the two integers in the regular expression, and uses the BASH_REMATCH array to access their values as well as the seq command to generate the numbers between the two.

(standard_in) 1: syntax error in bash script

I'm trying to generate some quasi random numbers to feed into a monte carlo simulation. I'm using bash. I seem to have hit a syntax error which I've narrowed down to being in this bit of code.
randno4=($RANDOM % 100001)
upper_limit4=$(echo "scale=10; 1*75.3689"|bc)
lower_limit4=$(echo "scale=10; 1*75.1689"|bc)
range4=$(echo "scale=10; $upper_limit4-$lower_limit4"|bc)
t_twall=`echo "scale=10; ${lower_limit4}+${range4}*${randno3}/100001" |bc`
echo "$t_twall"
Does anyone know why I the below output and not a value between 75.3689 and 75.1689 as that is what I would be expecting?
(standard_in) 1: syntax error
The first line should looks like :
randno4=$((RANDOM % 100001))
(( )) is bash arithmetic, with the leading $ , the value is substituted : $(( ))
When you wrote
randno4=( )
you try to feed an ARRAY with a arithmetic expression with the wrong syntax.
See http://wiki.bash-hackers.org/syntax/arith_expr
And finally, like Etan Reisner said, You also use $randno3 in the t_twall assignment line which is undefined

what is ":" called in bash? Colon usage as a pass statement [duplicate]

This question already has answers here:
What is the purpose of the : (colon) GNU Bash builtin?
(12 answers)
Closed 8 years ago.
See: What is the Bash equivalent of Python's pass statement
What is this ":" colon usage called? For example:
if [[ -n $STRING ]]; then
#printf "[INFO]:STRING: if -n string: STRING:$STRING \n"
:
else
printf "[INFO]:Nothing in the the string\n"
fi
To what that is, run help : in the shell. It gives:
$ help :
:: :
Null command.
No effect; the command does nothing.
Exit Status:
Always succeeds.
Very useful in one-liner infinite loops, for example:
while :; do date; sleep 1; done
Again, you could write the same thing with true instead of :, but this is shorter.
Interestingly:
$ help true
true: true
Return a successful result.
Exit Status:
Always succeeds.
According to this, the difference is that : is "Null command",
while true is "Returns a successful result".
Another difference is that true is usually a real binary:
$ which true
/usr/bin/true
On the other hand, which : gives nothing. (Which makes sense, being a "null command".)
Anyway, #Andy is right, this is duplicate of this other post, which explains it much better.

"Simple" arithmetic

Why can't I do this simple arithmetic operation and store it in a variable in bash shell? I've been struggling with this and playing around with () and $ symbols but no luck.
read t
let r=$(5/9)*$($t-32)
I get a: let: r=*: syntax error: operand expected (error token is "*")
When you are using the let statement, you don't need the dollar-sign, but single-quote the expression instead to keep the shell preprocessor from messing with your operators. Note that bash does not seem to be able to handle numbers which aren't integers, so the (5/9) expression will always be zero. Try the second let statement.
read -p 'Temp in Fahrenheit (no decimals): ' t
# let r='(5/9)*(t-32)' -- this doesn't work
let r='5*(t-32)/9'
echo "Centigrade: $r"
Try that :
read -p 'Type integer temp (Fahrenheit) >>> ' int
echo "$(( 5 * ( int - 32 ) / 9 )) Celcius"

atoi() like function in bash?

Imagine that I use a state file to store a number, I read the number like this:
COUNT=$(< /tmp/state_file)
But since the file could be disrupted, $COUNT may not contain a "number", but any characters.
Other than using regex, i.e if [[ $COUNT ~ ^[0-9]+$ ]]; then blabla; fi, is there a "atoi" function that convert it to a number(0 if invalid)?
EDIT
Finally I decided to use something like this:
let a=$(($a+0))
Or
declare -i a; a="abcd123"; echo $a # got 0
Thanks to J20 for the hint.
You don't need an atoi equivalent, Bash variables are untyped. Trying to use variables set to random characters in arithmetic will just silently ignore them. eg
foo1=1
foo2=bar
let foo3=foo1+foo2
echo $foo3
Gives the result 1.
See this reference
echo $COUNT | bc should be able to cast a number, prone to error as per jurgemaister's comments...
echo ${COUNT/[a-Z]*} | bc which is similar to your regex method but not prone to error.
case "$c" in
[0-9])...
You should eat the input string charwise.

Resources