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")
Related
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.
I am a beginner in Shell Programming.I am confused when I execute a program about if statement
Code is as follows
echo -n "word 1:"
read word1
echo -n "word 2:"
read word2
if test "$word1"="$word2"
then echo "Match"
fi
I found that regardless of whether I enter the same string,it always prints "Match"
That's what I want to ask,Thank you!
Add spaces around =:
if test "$word1" = "$word2"
Without them, you are using test on the expression "$word1"="$word2" to test if it is empty.
Silly, I know.
From man test:
-n STRING
the length of STRING is nonzero
STRING
equivalent to -n STRING
Just to be perfectly clear: First, $word1 and $word2 are replaced with the content of the variables, say hello and world to be original, so you get the string hello=world. So indeed, no matter what you will put in these variables, you will get a non-empty string (because of the =) and so the test will always pass.
Following is the code for extracting input from command line into bash script:
input=(*);
for i in {1..5..1}
do
input[i]=$($i);
done;
My question is: how to get $1, $2, $3, $4 values from input command line, where command line code input is:
bash script.sh "abc.txt" "|" "20" "yyyy-MM-dd"
Note: Not using for i in "${#}"
#!/bin/bash
for ((i=$#-1;i>=0;i--)); do
echo "${BASH_ARGV[$i]}"
done
Example: ./script.sh a "foo bar" c
Output:
a
foo bar
c
I don't know what you have against for i in "$#"; do..., but you can certainly do it with shift, for example:
while [ -n "$1" ]; do
printf " '%s'\n" "$1"
shift
done
Output
$ bash script.sh "abc.txt" "|" "20" "yyyy-MM-dd"
'abc.txt'
'|'
'20'
'yyyy-MM-dd'
Personally, I don't see why you exclude for i in "$#"; do ... it is a valid way to iterate though the args that will preserve quoted whitespace. You can also use the array and C-style for loop as indicated in the other answers.
note: if you are going to use your input array, you should use input=("$#") instead of input=($*). Using the latter will not preserve quoted whitespace in your positional parameters. e.g.
input=("$#")
for ((i = 0; i < ${#input[#]}; i++)); do
printf " '%s'\n" "${input[i]}"
done
works fine, but if you use input=($*) with arguments line "a b", it will treat those as two separate arguments.
If I'm correctly understanding what you're trying to do, you can write:
input=("$#")
to copy the positional parameters into an array named input.
If you specifically want only the first five positional parameters, you can write:
input=("${#:1:5}")
Edited to add: Or are you asking, given a variable i that contains the integer 2, how you can get $2? If that's your question, then — you can use indirect expansion, where Bash retrieves the value of a variable, then uses that value as the name of the variable to substitute. Indirect expansion uses the ! character:
i=2
input[i]="${!i}" # same as input[2]="$2"
This is almost always a bad idea, though. You should rethink what you're doing.
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
I have a large number of files which contain lines with matched braces. I do not care if the brackets are matched or not.
I'd like to check if any braces are nested, by checking which comes first after an opening bracket - a closing or another opening bracket. I assume that all brackets are matched, and that there is at most one outer-bracket per line. (Ie, [foo[bar]] is a valid line, [foo][bar] is not, because the second bracket pair isn't nested).
I can get everything inside a bracket pair from this question using 's/.*\[\([^]]*\)\].*/\1/g', but I'm unsure how to re-test the grabbed string for further matches.
For example, given the following string:
foo [ bar, [baz] ]
the steps I think I would take are:
Traverse from the left-hand side until I see an opening bracket. (If none is found, ignore the line).
Non-greedily search from the opening brace until either [ or ] is encountered. If [, brackets are nested, so return the line. If ],
Ideally I'd like a sed or unix-tool based solution, but others are acceptable (perl, for example). Any help would be appreciated.
Use the recursive regexp to check brackets match AND they are nested. Its no point to check nesting without syntax check cus this can break out checking result. For example:
my $regex = qr/\[([^\[\]]+?|(??{$regex}))*\]/;
if( $line =~ /^[^\[\]]*\[$regex\][^\[\]]*$/ ) #Valid
perl -ne 'print if /\[[^\]]*\[/' your_file
tested below:
> cat temp
foo [ bar, [baz] ]
foo [ bar, baz ]
foo [ bar ]
foo [ bar, baz] ]
foo bar, [baz] ]
> perl -ne 'print if /\[.*\[/' temp
foo [ bar, [baz] ]
>