I am going through this code. I would like to know what is meant by
-le in the following code segment.
if [ $stage -le 2 ]; then
In one of the questions it says that -le stands for <= of strings, but that is in Perl. Is it the same here as well?
Further, I would like to know if that $stage variable automatically gets updated. It has been initialized to 0 at the beginning, but later, how does that get incremented?
As is stated in the documentation:
integer comparison
(...)
-lt: is less than
if [ "$a" -lt "$b" ]
So it interprets the values of $a and $b (in your case $stage and 2) as integers and performs a comparison. If the first element is less than or equal to the second, the test succeeds and the then part will be executed.
As the documentation later states, one can use <= as well:
<=: is less than or equal to (within double parentheses)
(("$a" <= "$b"))
But then one uses double parentheses (as specified in the documentation).
-le is less than or equals to :
if [ $stage -le 2 ];
is same as:
stage <= 2
Related
I have an issue with some homework, so basically we were asked to create a bash script that takes a variable greater than 2 and gives back the same number of Fibonacci sequence numbers, i.o. if i were to give 5 it would print:
0 1 1 2 3
I have done some research and have come up with a unique idea i havent really seen anywhere online, (keep in mind i have some experience in python) but i have run into a problem, so basically i use a for expression in bash and 2 variables to calculate the sequence, i first manually set them to be 0 and 1 (the first and second numbers in the sequence) and then i add them together while changing their values,
So here is the code :
#!/bin/bash
a=0
b=1
for i in $(seq 1 $1);
do
if ["$a" -gt "$b"]
then
b=($a +$b)
echo "$b"
else
a=($a +$b)
echo $a
fi
done
I have many issues and expect to be completely out of context but i hope you get the general idea and can help guide me through the problem :/
It seems like i want something to be treated as a number but its treated as text... Not sure though, any help is highly appreciated
You were very close, some small syntactic changes and new initial values make it work.
Notes:
whitespace is important in bash, especially with special characters [ ] ;
also, calculation with integer number can be forced using let
Nice idea to use a comparison to do the calculation with just two variables :)
#!/bin/bash -
a=1
b=0
echo $a
for i in $(seq 1 $1);
do
if [ $a -gt $b ] ; then
let b=($a +$b)
echo $b
else
let a=($a +$b)
echo $a
fi
done
there is an arithmetic context in bash.
slight re-write can be
$ a=0; b=1;
for i in {1..9};
do c=$((a+b));
echo $c;
if ((a>b)); then b=$c; else a=$c; fi;
done
1
2
3
5
8
13
21
34
55
You need a space
if [ "$a" -gt "$b" ]
and you can do arithmetic evaluation using
b=$((a+b))
once the script runs, verify the result is what you are expecting (i.e. does it print 0?)
Use double parens to evaluate arithmetic expressions. Instead of:
if [ "$a" -gt "$b" ]
write:
if ((a > b))
and instead of:
($a + $b)
write:
$((a + b))
You can also write the for header this way:
for ((i = 0; i < $1; i++))
But this is not required in this case. Your loop then becomes:
for ((i = 0; i < $1; i++)); do
if ((a > b)); then
b=$((a + b))
echo "$b"
else
a=$((a + b))
echo "$a"
fi
done
#!/bin/bash
a=0
b=1
echo $a
c=$1
for i in $(seq 1 $((c-1)));
do
if [ $a -gt $b ]; then
b=$((a+b))
echo $b
else
a=$((a+b))
echo $a
fi
done
a=x b=x
If [ $a -eq $b ]
Then
echo "a is equal to b"
else
echo "a is not equal to b"
fi
Will the above code successfully output "a is equal to b" or there is some error?
When looking at your script, you use the single bracket version of the test command. This is in contrast to the double-bracket version which is a shell-internal test.
The test command states:
INTEGER1 -eq INTEGER2 : INTEGER1 is equal to INTEGER2
But you are not comparing integers, but strings (unless x could be a dummy name for something that could be an integer.). Hence, the test will fail.
$ [ x -eq x ]
[: integer expression expected: x
So your output will read a is not equal to b.
If you want to compare strings, then it is advised to use the operator =:
STRING1 = STRING2 : the strings are equal
-eq is for integer comparisons, but you are comparing strings. Use = (or bash-ism ==):
[ "$a" = "$b" ]
Like i did, quote variable expansions to prevent word splitting and pathname expansion.
how can i use a range of parameters in linux bash like 60-70 --> ($1 - $2)
case $1 in
$1-$2 )
echo "test"
shift;;
....) ....
Each case of a case statement is a pattern, but depending on the range, it can be tricky to specify a good pattern. 60-70, for instance, could be matched with
case $1 in
6[0-9] | 70) echo "test"
shift ;;
Essentially, you have to treat the numbers as digit strings, and match them as text. A range like 67-93, for instance, breaks down into the fairly unwieldy
6[7-9] | [78][0-9] | 9[0-3]
You can make a function for it.
When you have different ranges, make parameters like range(check, minimum, maximum).
Return 1 when check < minimimum, 0 when check inside boundaries
(or equal boundary) and 2 above:
range() {
if [ "$1" -lt "$2" ]; then
return 1
fi
if [ "$1" -gt "$3" ]; then
return 2
fi
return 0
}
When you want different returns for different ranges, change this function.
EDIT: Added quotes and made positive return values.
EDIT 2: changed function x into x()
This question differs in that the classic "use a function" answer WILL NOT work. Adding a note to an existing Alias question is equivalent to sending a suggestion e-mail to Yahoo.
I am trying to write macros to get around BASH's horrendous IF syntax. You know, the [, [[, ((...BASH: the PHP of flow control...just add another bracket. I'm still waiting for the "(((((((" form. Not quite sure why BASH didn't repurpose "(", as "(" has no real semantics at the if statement.
The idea is to have named aliases for [, [[ and (( , as each one of these durned test-ish functions has a frustratingly different syntax. I honestly can never remember which is which (how COULD you? It's completely ad hoc!), and good luck trying to google "[[".
I would then use the names as a mnemonic, and the alias to get rid of the completely awful differences in spacing requirements. Examples: "whatdoyoucallthisIf" for "((", "shif" (for shell if), "mysterydoublesquarebacketif" for that awful [[ thing which seems to mostly do the same thing as [, only it doesn't.
Thus, I MUST have something of the form:
alias IFREPLACEMENT="if [ \$# ]; then"
But obviously not \$#, which would just cement in the current argument list to the shell running the alias.
Functions will not work in this case, as the function:
function IFREPLACEMENT {
if [ $# ]; then
}
is illegal.
In CSH, you could say
alias abc blah blah !*
!1, etc. Is there ANYTHING in BASH that is similar (no, !* doesn't work in BASH)?
Or am [ "I just out of luck" ]; ?
As an aside, here are some of the frustrating differences involving test-ish functions in BASH that I am trying to avoid by using well-defined aliases that people would have to use instead of picking the wrong "[[", "[" or "((":
"((" is really creepy...if a variable contains the name of another variable, it's derferenced for as many levels as necessary)
"((" doesn't require a spaces like '[' and '[['
"((" doesn't require "$" for variables to be dereferenced
['s "-gt" is numeric or die. [[ seems to have arbitrary restrictions.
'[' and '[[' use ">" (etc) as LEXICAL comparison operators, but they have frustratingly different rules that make it LOOK like they're doing numeric comparisons when they really aren't.
for a variable: a="" (empty value), [ $a == 123 ] is a syntax error, but (( a == 123 )) isn't.
Sure, functions will work, but not like a macro:
function IFREPLACEMENT {
[[ "$#" ]]
}
IFREPLACEMENT "$x" = "$y" && {
echo "the same
}
FWIW, here's a brutal way to pass arguments to an alias.
$ alias enumerate='bash -c '\''for ((i=0; i<=$#; i++)); do arg=${!i}; echo $i $arg; done'\'
$ enumerate foo bar baz
0 foo
1 bar
2 baz
Clearly, because a new bash shell is spawned, whatever you do won't have any effect on the current shell.
Update: Based on feedback from #konsolebox, the recommendation is now to always use [[...]] for both simplicity and performance (the original answer recommended ((...)) for numerical/Boolean tests).
#Oliver Charlesworth, in a comment on the question, makes the case for not trying to hide the underlying bash syntax - and I agree.
You can simplify things with the following rules:
Always use [[ ... ]] for tests.
Only use [ ... ] if POSIX compatibility is a must. If available, [[ ... ]] is always the better choice (fewer surprises, more features, and almost twice as fast[1]).
Use double-quoted, $-prefixed variable references - for robustness and simplicity (you do pay a slight performance penalty for double-quoting, though1) - e.g., "$var"; see the exceptions re the RHS of == and =~ below.
Whitespace rules:
ALWAYS put a space after the initial delimiter and before the closing delimiter of conditionals (whether [[ / (( or ]] / )))
NEVER put spaces around = in variable assignments.
These rules are more restrictive than they need to be - in the interest of simplification.
Tips and pitfalls:
Note that for numeric comparison with [[ ... ]], you must use -eq, -gt, -ge, -lt, -le, because ==, <, <=, >, >= are for lexical comparison.
[[ 110 -gt 2 ]] && echo YES
If you want to use == with pattern matching (globbing), either specify the entire RHS as an unquoted string, or, at least leave the special globbing characters unquoted.
[[ 'abc' == 'a'* ]] && echo YES
Similarly, performing regex matching with =~ requires that either the entire RHS be unquoted, or at least leave the special regex chars. unquoted - if you use a variable to store the regex - as you may have to in order to avoid bugs with respect to \-prefixed constructs on Linux - reference that variable unquoted.
[[ 'abc' =~ ^'a'.+$ ]] && echo YES
re='^a.+$'; [[ 'abc' =~ $re ]] && echo YES # *unquoted* use of var. $re
An alternative to [[ ... ]], for purely numerical/Boolean tests, is to use arithmetic evaluation, ((...)), whose performance is comparable to [[ (about 15-20% slower1); arithmetic evaluation (see section ARITHMETIC EVALUATION in man bash):
Allows C-style arithmetic (integer) operations such as +, -, *, /, **, %, ...
Supports assignments, including increment and decrement operations (++ / --).
No $ prefix required for variable references.
Caveat: You still need the $ in 2 scenarios:
If you want to specify a number base or perform up-front parameter expansion, such as removing a prefix:
var=010; (( 10#$var > 9 )) && echo YES # mandate number base 10
var=v10; (( ${var#v} > 9 )) && echo YES # strip initial 'v'
If you want to prevent recursive variable expansion.
((...), curiously, expands a variable name without $ recursively, until its value is not the name of an existing variable anymore:
var1=10; var2=var1; (( var2 > 9 )) && echo YES
var2 expands to 10(!)
Has laxer whitespace rules.
Example: v1=0; ((v2 = 1 + ++v1)) && echo YES # -> $v1 == 1, $v2 == 2
Caveat: Since arithmetic evaluation behaves so differently from the rest of bash, you'll have to weigh its added features against having to remember an extra set of rules. You also pay a slight performance penalty1.
You can even cram arithmetic expressions, including assignments, into [[ conditionals that are based on numeric operators, though that may get even more confusing; e.g.:
v1=1 v2=3; [[ v1+=1 -eq --v2 ]] && echo TRUE # -> both $v1 and $v2 == 2
Note: In this context, by 'quoting' I mean single- or double-quoting an entire string, as opposed to \-escaping individual characters in a string not enclosed in either single- or double quotes.
1:
The following code - adapted from code by #konsolebox - was used for performance measurements:
Note:
The results can vary by platform - numbers are based on OS X 10.9.3 and Ubuntu 12.04.
[[ being nearly twice as fast as [ (factor around 1.9) is based on:
using unquoted, $-prefixed variable references in [[ (using double-quoted variable references slows things down somewhat)
(( is slower than [[ with unquoted, $-prefixed variable on both platforms: about 15-20% on OSX, around 30% on Ubuntu. On OSX, using double-quoted, $-prefixed variable references is actually slower, as is not using the $ prefix at all (works with numeric operators). By contrast, on Ubuntu, (( is slower than all ]] variants.
#!/usr/bin/env bash
headers=( 'test' '[' '[[/unquoted' '[[/quoted' '[[/arithmetic' '((' )
iterator=$(seq 100000)
{
time for i in $iterator; do test "$RANDOM" -eq "$RANDOM"; done
time for i in $iterator; do [ "$RANDOM" -eq "$RANDOM" ]; done
time for i in $iterator; do [[ $RANDOM -eq $RANDOM ]]; done
time for i in $iterator; do [[ "$RANDOM" -eq "$RANDOM" ]]; done
time for i in $iterator; do [[ RANDOM -eq RANDOM ]]; done
time for i in $iterator; do (( RANDOM == RANDOM )); done
} 2>&1 | fgrep 'real' | { i=0; while read -r line; do echo "${headers[i++]}: $line"; done; } | sort -bn -k3.3 | awk 'NR==1 { baseTime=substr($3,3) } { time=substr($3,3); printf "%s %s%%\n", $0, (time/baseTime)*100 }' | column -t
Outputs times from fastest to slowest, with slower times also expressed as a percentage of the fastest time.
So I have a bash script that needs to take an arbitrary number of command line arguments and put them into a single string
Example of what the user would type in:
give <environment> <email> <any number of integers separated by spaces>
give testing stuff#things.com 1 2 3 4 5
I want to get all of the arguments from $3 to $# and concat them into a string.
My (probably awful) solution right now is
if [ $# -gt 3 ]
then
env="env="$1
email="email="$2
entList=""
for i in {3..$#}
do
if [ $i -eq 3 ]
then
entList=$3
shift
fi;
if [ $i -gt 3 ]
then
entList=$entList","$3
shift
fi;
done
fi;
I handle the case of having only three arguments a bit differently, and that one works fine.
Final value of $entList given the example give testing stuff#things.com 1 2 3 4 5 should be: 1,2,3,4,5
Right now when i run this i get the following Errors:
/usr/local/bin/ngive.sh: line 29: [: {3..5}: integer expression expected
/usr/local/bin/ngive.sh: line 34: [: {3..5}: integer expression expected
Lines 29 and 34 are:
line 29: if [ $i -eq 3 ]
line 34: if [ $i -gt 3 ]
Any help would be appreciated.
You're on the right track. Here's my suggestion:
if [ $# -ge 3 ]; then
env="$1"
email="$2"
entlist="$3"
while shift && [ -n "$3" ]; do
entlist="${entlist},$3"
done
echo "entlist=$entlist"
else
echo "Arguments: $*"
fi
Note that variables should always be put inside quotes. I'm not sure why you were setting env=env=$1, but I suspect that if you want to recycle that value later, you should do it programatically rather than by evaluating the variable as if it were a statement, in case that was your plan.
Skip first three arguments using a subarray:
all=( ${#} )
IFS=','
threeplus="${all[*]:3}"
The reason you're getting those error messages is that in:
for i in {3..$#}
The brace expansion is performed before the parameter expansion and so the following if statement is evaluated as:
if [ {3..$#} -eq 3 ]
which isn't valid.
Change your for statement to use the C style:
for ((i = 3; i <= $#; i++))
Use this style for integer comparison:
if (( $# > 3 ))
and
if (( i == 3 ))
and
if (( i > 3 ))
Put your parameters inside the quotes:
env="env=$1"
email="email=$2"
and
entList="$entList,$3"
although the quotes aren't necessary since word splitting isn't performed on the right side of an assignment and you're not assigning special characters such as whitespace, semicolons, pipes, etc.