I currently have code as
if [[ "$FIRSTFLAG" == 1 ]] ; then
all_comp+=("FIRST")
fi
if [[ "$SECONDFLAG" == 1 ]] ; then
all_comp+=("SECOND")
fi
if [[ "$THIRDFLAG" == 1 ]] ; then
all_comp+=("THIRD")
fi
all_comp is just an array
So, im working on a solution to reduce the repetitive code
I know that we can use case here.
I wonder if there is a solution that can be done using array and for loop \
For example(I know its syntactically wrong)
names=("FIRST" "SECOND" "THIRD")
for i in $names[#]; do
if [[ ${i}FLAG == 1 ]]; then <- This line is the issue
all_comp+=("$i")
fi
done
So please tell me if there is a solution for such code example
You need to use indirect expansion by saving the constructed variable name, e.g. iflag=${i}FLAG, then you can use access the indirect expansion with ${!iflag}, e.g.
FIRSTFLAG=1
SECONDFLAG=0
THIRDFLAG=1
all_comp=()
names=("FIRST" "SECOND" "THIRD")
for i in ${names[#]}; do
iflag=${i}FLAG
if [[ ${!iflag} == 1 ]]; then
all_comp+=("$i")
fi
done
echo ${all_comp[#]} # Outputs: FIRST THIRD
Oh another answer, you can make use of the arithmetic expansion operator (( )) i.e.
FIRSTFLAG=1
SECONDFLAG=0
THIRDFLAG=1
all_comp=()
names=("FIRST" "SECOND" "THIRD")
for i in ${names[#]}; do
if (( ${i}FLAG == 1 )); then
all_comp+=("$i")
(( ${i}FLAG = 99 ))
fi
done
echo ${all_comp[#]} # FIRST THIRD
echo $FIRSTFLAG # 99
echo $SECONDFLAG # 0
echo $THIRDFLAG # 99
Reference:
https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion
my if condition with gt compare value and also if the value is null, but I want that gt compare null value, I just want that he compare only value
res=''
toto=5
if [[ "$toto" -gt "$res" ]]; then
...
else
...
fi
fi
solution is that, but not very good
if [[ ! -z "$res" ]]; then
if [[ "$toto" -gt "$res" ]]; then
...
else
...
fi
fi
Use &&.
if [[ ! -z "$res" && "$toto" -gt "$res" ]]
Other improvements you could make:
Replace ! -z with -n.
Remove unnecessary quotes.
Use ((...)) for numerical comparisons.
if [[ -n $res ]] && ((toto > res))
This Shellcheck-clean code handles empty $res in another way:
#! /bin/bash
res=''
toto=5
if [[ -z $res ]] ; then
: # $res is empty so don't compare with $toto
elif (( toto > res )); then
echo 'toto is greater than res'
else
echo 'toto is less than or equal to res'
fi
However, it's debatable whether it's better or worse than the "not very good" option suggested in the question. Deeper nesting is usually worse, but if-the-else chains are best avoided. The only advantage I would claim for the code in this answer is that it has a convenient place to put a helpful comment if one is useful.
I am puzzled about the verbose of quoting in the script. Take an example from the instruction I followed:
min_val=1
max_val=100
int=50
if [[ "$int" =~ ^-?[0-9]+$ ]]; then
if [[ "$int" -ge "$min_val" && "$int" -le "$max_val" ]]; then
echo "$int is within $min_val to $max_val."
else
echo "$int is out of range."
fi
else
echo "int is not an integer." >&2
exit 1
fi
Run it and come by:
$ bash test_integer3.sh
50 is within 1 to 100.
When I removed all the quoting in testing:
if [[ $int =~ ^-?[0-9]+$ ]]; then
if [[ $int -ge $min_val && $int -le $max_val ]]; then
echo "$int is within $min_val to $max_val."
else
echo "$int is out of range."
fi
else
echo "int is not an integer." >&2
exit 1
fi
It's still working properly.
$ bash test_integer3.sh
50 is within 1 to 100.
Why should live with the habit of writing redundant quoting?
The real problem comes when you start to use [ command over [[ in your scripts. [[ is bash's improvement to the [ command. It has several enhancements that make it a better choice to write scripts targeting bash.
One such improvement would be that you no longer have to quote variables because [[ handles empty strings and strings with white-space more intuitively. For example consider your script written with [ for the un-quoted case and for discussions sake, one of your variables is empty
#!/usr/bin/env bash
min_val=
max_val=
int=50
if [[ $int =~ ^-?[0-9]+$ ]]; then
if [ $int -ge $min_val -a $int -le $max_val ]; then
echo "$int is within $min_val to $max_val."
else
echo "$int is out of range."
fi
else
echo "int is not an integer." >&2
exit 1
fi
One thing to note is I've re-written the combined conditional using the -a syntax since [ does not support && operator within but could be combined using && as [ $int -ge $min_val ] && [ $int -le $max_val ]
You would see things going bad and seeing errors as below which means that one of the conditionals involving -le is gone wrong on seeing an empty string.
1_script.sh: line 7: [: -a: integer expression expected
50 is out of range.
whereas with same code for undefined variables and replacing the expression to use [[ would gracefully handle the empty strings to produce just an incorrect result as
50 is out of range.
So to sum it up, from the many advantages over using [[, the particular advantage on your case is to handle variables if there could be empty strings in your conditionals.
Quoting is used to to stop the word splitting. In the case above it is not necessary but consider a case like this: you have a directory and having theses files file1.txt, file2.txt, old.txt and file1 old.txt.
If you wish to remove the file file1 old.txt and run the command
rm file1 old.txt
then it will remove the file old.txt instead of what you expected.
In your piece of code you don't need quotes as you discovered. However, using quotes is considered "good practice" because unexpected things can happen without quotes. For example if you run the code with int equal to say "foo bar" you might get some strange results without quotes.
It is like the double and triple equals in JavaScript. You could probably get away with only double equals but some unexpected results might occur.
I am programming a little number-guessing game in bash.
This is the code so far:
#!/bin/bash
#Number guessing
read -p "Who are you? " name
echo "Welcome, ${name}!"
read -p "How high do you want to guess? " level
number=$(($RANDOM % $level))
((attempts++))
while [[ ${guess} != ${number} ]]
do
read -p "Whats the number? " guess
case $guess in
$number) echo "Correct! You needed ${attempts} attempts"
break
;;
*) echo "Wrong..."
((attempts++))
;;
esac
if [[ ${guess} > ${number} ]]
then
echo "Above."
else
echo "Below."
fi
done
This asks for the users name, how high the numbers that have to be guessed go and then generates a random number based on $level. What I want to do is a echo whether the entered number was above or below the guessed number. The if at the end of the while-loop doesnt work and the same if in the last case *) echo "Wrong" doesnt work either. Is it possible to have such an if in a case in a while loop or do you have another solution?
Have a nice day
!= and > are string comparison operators inside [[ ... ]]; you want integer comparison.
while [[ ${guess} -ne ${number} ]]
if [[ ${guess} -gt ${number} ]]
or better in bash, use an arithmetic statement where you can use the normal operators.
while (( guess != number ))
if (( guess > number ))
I would like my Bash script to print an error message if the required argument count is not met.
I tried the following code:
#!/bin/bash
echo Script name: $0
echo $# arguments
if [$# -ne 1];
then echo "illegal number of parameters"
fi
For some unknown reason I've got the following error:
test: line 4: [2: command not found
What am I doing wrong?
Just like any other simple command, [ ... ] or test requires spaces between its arguments.
if [ "$#" -ne 1 ]; then
echo "Illegal number of parameters"
fi
Or
if test "$#" -ne 1; then
echo "Illegal number of parameters"
fi
Suggestions
When in Bash, prefer using [[ ]] instead as it doesn't do word splitting and pathname expansion to its variables that quoting may not be necessary unless it's part of an expression.
[[ $# -ne 1 ]]
It also has some other features like unquoted condition grouping, pattern matching (extended pattern matching with extglob) and regex matching.
The following example checks if arguments are valid. It allows a single argument or two.
[[ ($# -eq 1 || ($# -eq 2 && $2 == <glob pattern>)) && $1 =~ <regex pattern> ]]
For pure arithmetic expressions, using (( )) to some may still be better, but they are still possible in [[ ]] with its arithmetic operators like -eq, -ne, -lt, -le, -gt, or -ge by placing the expression as a single string argument:
A=1
[[ 'A + 1' -eq 2 ]] && echo true ## Prints true.
That should be helpful if you would need to combine it with other features of [[ ]] as well.
Take note that [[ ]] and (( )) are keywords which have same level of parsing as if, case, while, and for.
Also as Dave suggested, error messages are better sent to stderr so they don't get included when stdout is redirected:
echo "Illegal number of parameters" >&2
Exiting the script
It's also logical to make the script exit when invalid parameters are passed to it. This has already been suggested in the comments by ekangas but someone edited this answer to have it with -1 as the returned value, so I might as well do it right.
-1 though accepted by Bash as an argument to exit is not explicitly documented and is not right to be used as a common suggestion. 64 is also the most formal value since it's defined in sysexits.h with #define EX_USAGE 64 /* command line usage error */. Most tools like ls also return 2 on invalid arguments. I also used to return 2 in my scripts but lately I no longer really cared, and simply used 1 in all errors. But let's just place 2 here since it's most common and probably not OS-specific.
if [[ $# -ne 1 ]]; then
echo "Illegal number of parameters" >&2
exit 2
fi
References
Bash Conditional Expressions
Conditional Constructs
Pattern Matching
Word Splitting
Filename Expansion (prev. Pathname Expansion)
Simple Commands
It might be a good idea to use arithmetic expressions if you're dealing with numbers.
if (( $# != 1 )); then
>&2 echo "Illegal number of parameters"
fi
>&2 is used to write the error message to stderr.
On []: !=, =, == ... are string comparison operators and -eq, -gt ... are arithmetic binary ones.
I would use:
if [ "$#" != "1" ]; then
Or:
if [ $# -eq 1 ]; then
If you're only interested in bailing if a particular argument is missing, Parameter Substitution is great:
#!/bin/bash
# usage-message.sh
: ${1?"Usage: $0 ARGUMENT"}
# Script exits here if command-line parameter absent,
#+ with following error message.
# usage-message.sh: 1: Usage: usage-message.sh ARGUMENT
A simple one liner that works can be done using:
[ "$#" -ne 1 ] && ( usage && exit 1 ) || main
This breaks down to:
test the bash variable for size of parameters $# not equals 1 (our number of sub commands)
if true then call usage() function and exit with status 1
else call main() function
Things to note:
usage() can just be simple echo "$0: params"
main can be one long script
Check out this bash cheatsheet, it can help alot.
To check the length of arguments passed in, you use "$#"
To use the array of arguments passed in, you use "$#"
An example of checking the length, and iterating would be:
myFunc() {
if [[ "$#" -gt 0 ]]; then
for arg in "$#"; do
echo $arg
done
fi
}
myFunc "$#"
This articled helped me, but was missing a few things for me and my situation. Hopefully this helps someone.
Here a simple one liners to check if only one parameter is given otherwise exit the script:
[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit
There is a lot of good information here, but I wanted to add a simple snippet that I find useful.
How does it differ from some above?
Prints usage to stderr, which is more proper than printing to stdout
Return with exit code mentioned in this other answer
Does not make it into a one liner...
_usage(){
_echoerr "Usage: $0 <args>"
}
_echoerr(){
echo "$*" >&2
}
if [ "$#" -eq 0 ]; then # NOTE: May need to customize this conditional
_usage
exit 2
fi
main "$#"
In case you want to be on the safe side, I recommend to use getopts.
Here is a small example:
while getopts "x:c" opt; do
case $opt in
c)
echo "-$opt was triggered, deploy to ci account" >&2
DEPLOY_CI_ACCT="true"
;;
x)
echo "-$opt was triggered, Parameter: $OPTARG" >&2
CMD_TO_EXEC=${OPTARG}
;;
\?)
echo "Invalid option: -$OPTARG" >&2
Usage
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
Usage
exit 1
;;
esac
done
see more details here for example http://wiki.bash-hackers.org/howto/getopts_tutorial
You should add spaces between test condition:
if [ $# -ne 1 ];
then echo "illegal number of parameters"
fi
I hope this helps.