$ is compulsory in case of conditional statement-linux [ ]? - linux

I have one doubt I have started learning Linux and learn about the way we can perform mathematical operations. for example
a) using expr > $ and space are mandatory.
example: sum= expr $a + $b
b) using let keyword > $ is optional but we should use space.
c) using (()) > both $ and space is optional.
d) using [ ] > both $ and space is optional.
so now I have written one simple if statement.
#! /bin/bash
read -p "Please enter username:" name
if [ name = sunny ]
then
echo "hello Sunny is available. "
fi
echo "Sunny is busy-remaining line code"
So inside a square bracket, I am doing arithmetic operation right so why here do I need to use the $ symbol to get the name value.
Note If I'll use if [ $name = sunny ] I'm getting expected result.
Any help/suggestion on this would be highly appreciated.

The rule is relatively simple: $ and spaces are optional in arithmetic context, but not in string contexts.
This is because if you expect a number and see foo, you can safely assume that it must be a variable because it sure isn't a number. This is not possible for strings.
Arithmetic context includes:
Arithmetic expansion $((here)) and arithmetic commands ((here))
Integer comparators in [[ .. ]] (but not [ .. ]), such as [[ here -eq here ]]§. Note in particular that = is a string comparator.
Values assigned to variables declared as integers: declare -i foo=here§
Indices of indexed arrays: ${array[here]}
Arguments to let: let here§
Some other more niche constructs like ${str:here:here}, $[here]
In your case, you are using the test command aka [, which (mostly) does not treat anything as an arithmetic expression. This is why you need the $ to differentiate between the literal string name and the value of the variable name.
§ These words are delimited by spaces so one would terminate the expression, but this does not change the fact that spaces are optional. They just need to be escaped to be considered part of the expression.

Related

[:== :Unexpected operator, too many argument, binary operator expected

See below:
1.
if [ $var1 == "result" ]
2,3.
if [ -z $var ]
I met this warning in bash version 4.4
Does anyone know this? Please explain detail. Thanks.
Try:
"$var1" == "result"
And:
-z "$var1"
When $var1 is non existent the tests do not work, that is easily fixed by surrounding the tested variables with "" so that if they are non existent you compare to the empty variable
The problem is the fact your $var contains spaces. So, those spaces are going to appear in the if, like if they were separating parameters/values. To solve, use "$var", where all spaces are joined into a unique value.
So, if var1 was bound to foo bar in the shell, in [ $var1 = "result" ] the inside of the test(1) is expanded as 4 arguments: foo, bar, =, result but = is binary and wants only one argument on each side (so you've got an error like "too many arguments" or "binary operator expected")

How do you interpret ${VAR#*:*:*} in Bourne Shell

I am using Bourne Shell. Need to confirm if my understanding of following is correct?
$ echo $SHELL
/bin/bash
$ VAR="NJ:NY:PA" <-- declare an array with semicolon as separator?
$ echo ${VAR#*} <-- show entire array without separator?
NJ:NY:PA
$ echo ${VAR#*:*} <-- show array after first separator?
NY:PA
$ echo ${VAR#*:*:*} <-- show string after two separator
PA
${var#pattern} is a parameter expansion that expands to the value of $var with the shortest possible match for pattern removed from the front of the string.
Thus, ${VAR#*:} removes everything up and including to the first :; ${VAR#*:*:} removes everything up to and including the second :.
The trailing *s on the end of the expansions given in the question don't have any use, and should be avoided: There's no reason whatsoever to use ${var#*:*:*} instead of ${var#*:*:} -- since these match the smallest amount of text possible, and * is allowed to expand to 0 characters, the final * matches and removes nothing.
If what you really want is an array, you might consider using a real array instead.
# read contents of string VAR into an array of states
IFS=: read -r -a states <<<"$VAR"
echo "${states[0]}" # will echo NJ
echo "${states[1]}" # will echo NY
echo "${#states[#]}" # count states; will emit 3
...which also gives you the ability to write:
printf ' - %s\n' "${states[#]}" # put *all* state names into an argument list

Linux for loop function in .bashrc [duplicate]

This question already has answers here:
How do I iterate over a range of numbers defined by variables in Bash?
(20 answers)
Variables in bash seq replacement ({1..10}) [duplicate]
(7 answers)
Closed 5 years ago.
#!/bin/sh
for i in {1..5}
do
echo "Welcome"
done
Would work, displays Welcome 5 times.
#!/bin/sh
howmany=`grep -c $1 /root/file`
for i in {1..$howmany}
do
echo "Welcome"
done
Doesn't work! howmany would equal 5 as that is what the output of grep -c would display. $1 is parameter 1 which is specific when running the script.
Any ideas?
Workarounds for not being able to use variables in a sequence brace expression:
If the intent is merely to iterate over numbers in a range - as in the OP's case - the best choice is not to use brace expansion, but instead use bash's C-style loop - see user000001's answer.
If the specific numbers aren't important and you simply need to execute a loop body a specified number of times, Cole Tierney's answer is an option.
If use of brace expansion is desired nonetheless:
If you do NOT need the numbers in the list to have a prefix or postfix, use the seq utility with an unquoted command substitution (small caveat: seq is NOT a POSIX utility, but it is widely available); e.g.
echo $(seq 3) -> 1 2 3; start number 1 implied
echo $(seq -f '%02.f' 3) -> 01 02 03 - zero-padded
echo $(seq 2 4) -> 2 3 4; explicit start and end numbers
echo $(seq 1 2 5) -> 1 3 5; custom increment (the 2 in the middle)
If you DO need the numbers in the list to have a prefix or postfix, you have several choices:
Use the seq utility with its -f option for providing a printf-style format string (as used above for zero-padding), or pure Bash workarounds based on eval (extra care needed!) or building an array in a loop, all of which are detailed in this answer.
You could also consider implementing the functionality generically, such as by writing a custom shell function or a custom script with utilities such as awk or perl.
Example of safe use of eval with variables driving a sequence brace expression:
The variables are validated beforehand, to make sure they contain decimal integers.
from=1 to=3 # sample values
# Ensure that $from and $to are decimal numbers and abort, if they are not.
(( 10#$from + 10#$to || 1 )) 2>/dev/null || { echo "Need decimal integers" >&2; exit 1; }
eval echo "A{$from..$to}" # -> 'A1 A2 A3'
General overview of brace expansion
The main purpose of brace expansion is to expand to a list of tokens with each token having an optional prefix and/or postfix; brace expansions must be unquoted and come in 2 flavors:
a fixed series (list) of comma-separated strings - variables supported
specifies and expands to a fixed number of tokens (2 or more); e.g.:
echo A{b,c,d} -> Ab Ac Ad, i.e., 3 tokens, as implied by the number of args.
echo {/,$HOME/}Library e.g., -> /Library /User/jdoe/Library
Variable references - and even globs - are supported, but note that they get expanded after brace expansion, in its result, in the course of normal evaluation.
a sequence expression (range) with .., typically numerical - variables NOT supported
expands to a variable number of tokens, driven by literal start and end points (for historical reasons, use of variables is NOT supported - see the comments on user000001's answer):
[rare] strings: only single English letters allowed; e.g. {a..c}
numbers: decimal integers only; e.g., {1..10}, {10..1}, {-1..2}
example with prefix and postfix: A{1..3}# -> A1# A2# A3#
broken example with variables: {$from..$to} # !! FAILS - $from and $to are interpreted as literals and therefore not recognized as either a single letter or a decimal integer - no brace expansion is performed (see below).
by contrast, using variables does work in zsh and ksh.
bash 4+ adds two features:
optional increment step value:
echo A{1..5..2} -> A1 A3 A5 - numbers incremented by 2
ability to zero-pad:
echo A{001..003} -> A001 A002 A003
An invalid brace expression is not expanded (treated like a regular unquoted string, with { and } treated as literals):
echo {} -> '{}' - invalid as a brace expr.: at least 2 ,-separated tokens needed
This allows the use of unquoted {} with find, for instance.
echo {1..$to} -> '{1..<value-of-$to>}' - invalid as a brace expr. in bash: variables not supported; however, valid in ksh and zsh.
(fish, by contrast, expands any {...} sequence; similarly, zsh has option BRACE_CCL (OFF by default) for expanding individual characters inside {..}, which effectively causes expansion of any nonempty {...} sequence.)
The brace expansion is evaluated before the variables are expanded. You need a c-style for loop instead:
for ((i=1;i<=howmany;i++))
do
echo "Welcome"
done
create a sequence to control your loop
for i in $(seq 1 $howmany); do
echo "Welcome";
done
The problem is that the "brace expansion" is performed before the "variable expansion"
for i in $(seq 1 $howmany)
works as #damienfrancois said, or, if you would like:
for i in $(eval echo "{$start..10}")
probably does, but don't use it for everyone's sanity.
You could also use a while loop:
while ((howmany--)); do
echo "Welcome"
done
We could also use eval in this case:
howmany=`grep -c $1 /root/file`
for i in $(eval echo {1..$howmany}); do
echo "Welcome"
done

Access bash positional parameter through variable

How to access bash positional parameter through a variable?
e.g. I have a variable "pos", which can be anything between 1 to 6 (say).
If pos==1, I want to do: echo $1
If pos==2, I want to do: echo $2
So on.
Intuitively, I want to do something like: echo $$pos.
I want to do it in one line.
Use variable indirection:
echo "${!pos}"
Here are several solutions. Some may need a recent version of bash, others may still work with a very old one.
Let us set up first our environment...
$ set first second third fourth
$ pos=3
Substring expansion
$ printf 'Parameter %d is "%s"\n' "$pos" "${#:pos:1}"
Parameter 3 is "third"
This is very flexible:
Can match several consecutive parameters: "${#:pos:2}"
Can match all the remaining parameters starting from pos: "${#:pos}"
Works with literals, variable pos is not necessary: "${#:3:2}"
Works also with any arithmetic expression: "${#:(pos-1)*2:1}"
Works also with negative numbers (counts down from the last):
$ printf 'Last parameter is "%s"\n' "${#: -1}" # Mind the space!
Last parameter is "fourth"
$ printf 'Parameter %d is "%s" to last\n' "$pos" "${#: -pos:1}"
Parameter 3 is "second" to last
Intermediary array
Bash's arrays are very flexible. Just put your script parameters into an array and access its elements with pos:
$ args=( "$0" "$#" )
$ printf 'Parameter %d is "%s"\n' "$pos" "${args[pos]}"
Advantages:
Straightforward array notation array[pos]
Array index may be negative (counting down from the last element)
Array index may be any arithmetic expression: "${args[(pos-1)*2]}"
Can be combined with substring expansion, so all its advantages apply here too: "${args[#]:pos:2}"
Indirect expansion
The ${!var} syntax fetches the content of var and the whole is substituted with $content. If var contains a number n, the whole is substituted with the nth positional parameter.
$ printf 'Parameter %d is "%s"\n' "$pos" "${!pos}"
Parameter 3 is "third"
Drawbacks:
Less flexible than above solutions
eval
Father of all evils, eval may still be useful if you shell doesn't support any of the above:
$ eval "param=\${$pos}" # Just eval the assignment, nothing more
$ printf 'Parameter %d is "%s"\n' "$pos" "$param"
Parameter 3 is "third"
Advantages:
Works in any Bourne shell
Flexibility: with eval you can do anything (and this is also the problem with it)
Drawbacks:
eval is a beast that is difficult to tame. Limit its use to the strict minimum. For example, only eval the assignment given above in example and nothing more (this necessitates the temporary variable param). Of course, sanity check of pos is mandatory but this is also the case for the other commands given here.
Subshell
Since subshells inherit the positional parameters, and since what happens in subshells stays in subshells, we can use these properties to shift the parameters:
$ printf 'Parameter %d is "%s"\n' "$pos" "$(shift $((pos-1)); printf %s "$1")"
Parameter 3 is "third"
$ echo "$1" # Check that parameters weren't shifted in parent shell
first
Advantages:
Works in any Bourne shell
Arithmetic operations on pos

How can I repeat a string N times in Perl?

In Python, if I do this:
print "4" * 4
I get
> "4444"
In Perl, I'd get
> 16
Is there an easy way to do the former in Perl?
$ perl -e 'print "4" x 4; print "\n"'
4444
The x operator is documented in perldoc perlop. Here binary means an operator taking two arguments, not composed of bits, by the way.
Binary "x" is the repetition operator. In scalar context or if the
left operand is not enclosed in parentheses, it returns a string consisting
of the left operand repeated the number of times specified by the right
operand. In list context, if the left operand is enclosed in parentheses
or is a list formed by "qw/STRING/", it repeats the list. If the right
operand is zero or negative, it returns an empty string or an empty
list, depending on the context.
print '-' x 80; # Print row of dashes
print "\t" x ($tab/8), ' ' x ($tab%8); # Tab over
#ones = (1) x 80; # A list of 80 1’s
#ones = (5) x #ones; # Set all elements to 5
perl -e is meant to execute Perl code from the command line:
$ perl --help
Usage: perl [switches] [--] [programfile] [arguments]
-e program one line of program (several -e's allowed, omit programfile)
In Perl, you want to use the "x" operator.
Note the difference between
"4" x 4
and
("4") x 4
The former produces a repeated string:
"4444"
the latter a repeated list:
("4", "4", "4", "4")
It's very similar in Perl
print "4" x 4;
FWIW, it’s also print 4 x 4 in Perl.
In general, in Perl, operators are monomorphic, ie. you have different sets of operators for string semantics, for numeric semantics, for bitwise semantics, etc., where it makes sense, and the type of the operands largely doesn’t matter. When you apply a numeric operator to a string, the string is converted to a number first and you get the operation you asked for (eg. multiplication), and when you apply a string operator to a number, it’s turned into a string and you get the operation you asked for (eg. repetition). Perl pays attention to the operator first and the types of the operands only second – if indeed it pays them any mind at all.
This is the opposite of Python and most other languages, where you use one set of operators, and the types of the operands determine which semantics you’ll actually get – ie. operators are polymorphic.
If you want to print 10 character "A"s, you can also do this
perl -e 'print "A" x 10'; echo
Example with output
user#linux:~$ perl -e 'print "A" x 10'; echo
AAAAAAAAAA
user#linux:~$
All answers, given so far, missed mentioning that the operator x does not only work on string literals, but also on variables that are strings or expressions that evaluate to strings like
use feature 'say';
my $msg = "hello ";
say $msg x 2;
say chr(33) x 3;
like this
hello hello
!!!
and, even more important, x does an automatic conversion of expressions into strings if they aren't already (thanks to ggorlen for pointing me into that direction!). So for example
say 4 x 2;
say [$msg] x 2;
will result in something like the following as output
44
ARRAY(0x30ca10)ARRAY(0x30ca10)
Came this way looking for an answer. Didn't quite find what I was looking for so I thought I'd share my learning. I wanted to compose dynamic SQL CRUD statements with the appropriate number of placeholders.
$table = "ORDERS";
#fields = ("ORDER_ID", "SALESMAN_ID", "CUSTOMER_ID", "ORDER_DATE", "STATUS");
$sql = "INSERT INTO $table (" . join(',', #fields) . ') VALUES (' . '?,' x (#fields - 1) . '?)';
print $sql;
The output looks like this...
INSERT INTO ORDERS (ORDER_ID,SALESMAN_ID,CUSTOMER_ID,ORDER_DATE,STATUS) VALUES (?,?,?,?,?)

Resources