Linux Bash's rule for determing a variable name Greedy or Non-Greedy - linux

If you run the following bash script on Ubuntu 12.04:
t="t"
echo "1st Output this $15"
echo "2nd this $test"
The output is:
1st Output this 5
2nd this
How can the first echo takes $1 as a variable name (non-greedy) interpreting it as ${1}5 while the second echo takes $test as a variable name (greedy) instead of interpreting it as ${t}est?

There are two parts to your question:
$15 would always be interpreted as $1, i.e. the first positional parameter1, concatenated with 5. In order to use the fifteenth positional parameter, you'd need to say ${15}.
$test would be interpreted as variable test. So if you want it to be interpreted as $t concatenated with est, then you need to say ${t}est
1Quoting from info bash:
When a positional parameter consisting of more than a single digit is
expanded, it must be enclosed in braces (see EXPANSION below).
...
EXPANSION
Expansion is performed on the command line after it has been split into
words. There are seven kinds of expansion performed: brace expansion,
tilde expansion, parameter and variable expansion, command substitu‐
tion, arithmetic expansion, word splitting, and pathname expansion.
You may also want to refer to:
What's the difference between ${varname} and $varname in a shell scripts

Related

shell variable $? not working inside in an at heredoc [duplicate]

Is it possible to create a heredoc that does not become subject to variable expansion?
e.g.
cat <<-EOF > somefile.sh
Do not print current value of $1 instead evaluate it later.
EOF
Update I am aware of escaping by \. My actual heredoc has many variables in it - and it is error prone and tedious to escape all of them.
Quote the delimiter:
cat <<-"EOF" > somefile.sh
Do not print current value of $1 instead evaluate it later.
EOF
This results in:
$ cat somefile.sh
Do not print current value of $1 instead evaluate it later.
Documentation
The format of here-documents is:
<<[-]word
here-document
delimiter
No parameter and variable expansion, command substitution, arithmetic expansion, or pathname expansion is performed on word. If
any characters in word are quoted, the delimiter is the result of
quote removal on word, and the lines in the here-document are
not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command
substitution, and arithmetic expansion, the character sequence
\ is ignored, and \ must be used to quote the characters \,
$, and `.
If the redirection operator is <<-, then all leading tab characters are stripped from input lines and the line containing
delimiter. This allows here-documents within shell scripts to be
indented in a natural fashion. [Emphasis added.]
Put backlash before the $ sign
$ VAR=XXX
$ cat << END
> dk
> \$VAR
> END
dk
$VAR

Prompt user confimation in Bash shell script for GNU Bash 4.3

I'm trying to create a shell script that sets up my Ubuntu server for a Laravel app. The user is asked to confirm before proceeding with the following code taken from here:
How do I prompt a user for confirmation in bash script?
#!/bin/sh
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
echo "\n ${GREEN}Enter the folder name for the Laravel application: ${NC}"
read APP_NAME
read -r -p "Are you sure? [y/N] " response
response=${response,,} # tolower
if [[ $response =~ ^(yes|y)$ ]]
then
echo "Installing dependencies..."
else
exit
fi
I'm getting this error:
Bad substitution
on the line
response=${response,,} # tolower
This is a case modification substitution. Here is the description (from the Bash manual on shell parameter expansion):
${parameter^pattern}
${parameter^^pattern}
${parameter,pattern}
${parameter,,pattern}
This expansion modifies the case of alphabetic characters in
parameter. The pattern is expanded to produce a pattern just as in
filename expansion. Each character in the expanded value of parameter
is tested against pattern, and, if it matches the pattern, its case is
converted. The pattern should not attempt to match more than one
character. The ‘^’ operator converts lowercase letters matching
pattern to uppercase; the ‘,’ operator converts matching uppercase
letters to lowercase. The ‘^^’ and ‘,,’ expansions convert each
matched character in the expanded value; the ‘^’ and ‘,’ expansions
match and convert only the first character in the expanded value. If
pattern is omitted, it is treated like a ‘?’, which matches every
character. If parameter is ‘#’ or ‘*’, the case modification operation
is applied to each positional parameter in turn, and the expansion is
the resultant list. If parameter is an array variable subscripted with
‘#’ or ‘*’, the case modification operation is applied to each member
of the array in turn, and the expansion is the resultant list.
This works on bash >= 4.0.
Alternatively, you can use
response=$(echo "$response" | tr '[:upper:]' '[:lower:]')
Thanks to David C. Rankins help in the comments section this issue was resolved by changing:
#!/bin/sh
to
#!/bin/bash
and changing
echo "string data"
to
echo -e "string data"

Zenity --text with string variable

I'm wondering how I can pass a string containing spaces to Zenity for the text argument as my current method is truncating/failed to evaluate all the text after the first space.
Here is an MVP which shows the issue:
Script
#!/bin/bash
a="test test test"
test_func() {
echo "$#"
$(zenity --info --text "test test")
$(zenity --info --text "$#")
}
test_func ${a}
Output
$> test test test
$> (zenity info window with test test as text)
$> (zenity info window with test) *** should contain "test test test"
Use "$*" instead of "$#".
Manual for "$#" (emphasis mine):
Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$#" is equivalent to "$1" "$2" ... If the double-quoted expan sion occurs within a word, the expansion of the first parameter is joined with the beginning part of the orig‐ inal word, and the expansion of the last parameter is joined with the last part of the original word. When there are no positional parameters, "$#" and $# expand to nothing (i.e., they are removed).
And for "$*":
Expands to the positional parameters, starting from one. When the expansion is not within double quotes, each positional parameter expands to a separate word. In contexts where it is performed, those words are subject to further word splitting and pathname expansion. When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFS special variable. That is, "$*" is equivalent to "$1c$2c...", where c is the first character of the value of the IFS variable. If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators.
Alternatively, you could quote ${a} to pass a as a single argument instead of splitting on spaces.

decrypting a variable in a scripting environment of Linux

What does $# in unix shell script signify. For example:
A__JOB="$CLASS $#"
where $CLASS has my java class file name. So what might be the meaning of
$#.
What did I do?
I Googled :) but $# seems to be complex query for ir or maybe i do not know how to search google for special characters.
$# is the value of all arguments passed.
For example, if you pass:
./script A B C D
then "$#" will be equal to "A" "B" "C" "D"
So it looks like the purpose is to passe all the arguments passed to the script directly to the java program.
From bash manual:
# Expands to the positional parameters, starting from one. When
the expansion occurs within double quotes, each parameter
expands to a separate word. That is, "$#" is equivalent to "$1" "$2" ... If the double-quoted expansion occurs
within a
word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the
last parameter is joined with the last part of the original word. When there are no positional parameters, "$#" and $# expand
to nothing (i.e., they are removed).

How to echo "$x_$y" in Bash script?

It is very interesting that if you intend to display 0_1 with Bash using the code
x=0
y=1
echo "$x_$y"
then it will only display
1
I tried echo "$x\_$y" and it doesn't work.
How can I echo the form $x_$y? I'm going to use it on a file name string.
Because variable names are allowed to have underscores in them, the command:
echo "$x_$y"
is trying to echo ${x_} (which is probably empty in your case) followed by ${y}. The reason for this is because parameter expansion is a greedy operation - it will take as many legal characters as possible after the $ to form a variable name.
The relevant part of the bash manpage states:
The $ character introduces parameter expansion, command substitution, or arithmetic expansion.
The parameter name or symbol to be expanded may be enclosed in braces, which are optional but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.
When braces are used, the matching ending brace is the first } not escaped by a backslash or within a quoted string, and not within an embedded arithmetic expansion, command substitution, or parameter expansion.
Hence, the solution is to ensure that the _ is not treated as part of the first variable, which can be done with:
echo "${x}_${y}"
I tend to do all my bash variables like this, even standalone ones like:
echo "${x}"
since it's more explicit, and I've been bitten so many times in the past :-)
This way:
$ echo "${x}_${y}"
0_1
wrap it in curly braces:
echo "${x}_${y}"
Just to buck the trend, you can also do this:
echo $x'_'$y
You can have quoted and unquoted parts next to each other with no space between. And since ' isn't a legal character for a variable name, bash will substitute only $x. :)

Resources