Bad substitution in Bash [duplicate] - linux

This question already has answers here:
Dynamic variable names in Bash
(19 answers)
Closed last month.
I have written this code in which m getting bad substitution error. Please help here.
#!/bin/bash
function detect_value(){
#some operation gives u a value
echo "ABCD"
}
ABCD_COUNT=26
echo "${"$(detect_value)"_COUNT}"
bash run.sh
run.sh: line 12: ${"$(detect_value)"_COUNT}: bad substitution

The name of a parameter has to be static, not produced by another expression. To do what you want, you need to use indirect parameter expansion, where the name of the parameter to expand is stored in another parameter.
t=$(detect_value)_COUNT
echo ${!t}
Depending on your use case, you might want to use an associative array instead.
declare -A counts
counts[ABCD]=26
detect_value () {
echo ABCD
}
echo "${counts[$(detect_value)]}"

Related

Replace hyphens with underscores in bash script [duplicate]

This question already has answers here:
Replacing some characters in a string with another character
(6 answers)
Difference between sh and Bash
(11 answers)
Closed 4 years ago.
Trying to write a bash script and in one part of it I need to take whatever parameter was passed to it and replace the hyphens with underscores if they exist.
Tried to do the following
#!/usr/bin/env bash
string=$1
string=${string//-/_}
echo $string;
It's telling me that this line string=${string//-/_} fails due to "Bad substitution" but it looks like it should do it? Am I missing something?
There is nothing wrong with your script, and it should work in modern versions of Bash.
But just in case you can simplify that to :
#!/bin/bash
echo "$1" | tr '-' '_'
This is in case that parameter substitution does not work ( which seems to be your case ).
Regards!

File read not interpreting Environment variables [duplicate]

This question already has an answer here:
Expand ENV variable of a string, run command and store in variable?
(1 answer)
Closed 6 years ago.
When I try to read a file which contains a environment variable HOSTNAME.
It is not interpreting its value while reading the file.
For example, if my hostname is linux1.com.
When I try to read a sample file(Test.txt) below
/var/log/$HOSTNAME
using the code below
while read line
do
ls -l $line
done < Test.txt
I am expecting it to interpet the $HOSTNAME variable and print it. But it is not working. It is directly doing ls -l /var/log/$HOSTNAME, instead of
ls -l /var/log/linux1.com
The same command is intrepreting the hostname when I run this command in shell.
Any leads on this is highly appreicated.
Thanks.
Parameter expansion is not recursive. When $line is expanded, its value is subject to pathname expansion (globbing) and word-splitting, but not further parameter expansions.

How is Bash interpreting these statements? [duplicate]

This question already exists:
Assign and use of a variable in the same subshell [duplicate]
Closed 7 years ago.
$ a=one; echo $a
one
$ a=two echo $a
one
$ echo $a
one
The value of a will be initialized to 'two' but why is it displaying 'one'?
Same line initialization of env variables is only available for sub shells.
It will be more evident from these examples:
unset a
a=two echo "<$a>"
<>
a=two bash -c 'echo "<$a>"'
<two>
echo "<$a>"
<>
In the 2nd snippet it prints <two> because we're spawning a sub-shell using bash -c.
This is due to the order in which the shell processes substitutions and variables. According to POSIX rules for a shell:
When a given simple command is required to be executed (that is, when
any conditional construct such as an AND-OR list or a case statement
has not bypassed the simple command), the following expansions,
assignments, and redirections shall all be performed from the
beginning of the command text to the end:
The words that are recognized as variable assignments or redirections according to Shell Grammar Rules are saved for processing
in steps 3 and 4.
The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion, the first
field shall be considered the command name and remaining fields are
the arguments for the command.
Redirections shall be performed as described in Redirection.
Each variable assignment shall be expanded for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and
quote removal prior to assigning the value.
That means that the variable assignment is performed after parameter expansion has occurred, e.g. $a is expanded before the a= variable assignment for that command has been processed. The new value of a will be passed into the execution environment of the command but it won't be of any use in this case with echo.
You can perhaps see better what's going on if you use a variable that does not already exist.
$ unset a
$ a=two echo \($a\)
()
$ a=one
$ a=two echo \($a\)
(one)
Here's an example where you can see that the variable assignment is passed to the execution environment of the command that is executed:
$ a=one
$ a=two python -c 'import os;print(os.environ["a"])'
two

"invalid arithmetic operator" in shell

cat test.sh
#!/bin/bash
key="index";
arr[$key]="val"
echo ${arr[${key}]}
/bin/bash-x test.sh
+ key=index
+ arr[$key]=val
+ echo val
val
then I modify the test.sh:
#!/bin/bash
key="index.index";
arr[$key]="val"
echo ${arr[${key}]}
/bin/bash -x test.sh
+ key=index.index
+ arr[$key]=val
test.sh: line 3: index.index: syntax error: invalid arithmetic operator (error token is ".index")
test.sh: line 4: index.index: syntax error: invalid arithmetic operator (error token is ".index")
why this error appears, any suggestion will be appreciate!
This:
key="index";
arr[$key]="val"
echo ${arr[${key}]}
only appears to work. Since arr is an ordinary array, not an associative array, it can only be indexed by non-negative integer values.
Consider this valid code:
index=42
key="index"
arr[$key]="val"
echo ${arr[42]}
echo ${arr[index]}
echo ${arr[$index]}
echo ${arr['index']}
echo ${arr["index"]}
echo ${arr[\index]}
All the echo statements print val. Since the index is treated as an arithmetic expression, it can refer to a variable (in this case, $index) either with or without the $ prefix -- even if it's a quoted string.
In your code, where you never assigned a value to $index, ${arr[${key}]} expands to ${arr[index]}, which is equivalent to ${arr[$index]}, which is treated (by default) as equivalent to ${arr[0]}.
(If you have set -o nounset, then references to unset variables are treated as errors, and your code will produce an error message.)
Your second chunk of code:
key="index.index";
arr[$key]="val"
echo ${arr[${key}]}
is invalid because index.index is not a valid variable name -- even though you probably meant it to be just a string used as an array index.
If you want arr to permit arbitrary strings as indices, it needs to be an associative array. You can create a non-associative array simply by assigning to it (or by using declare -a), but an associative array can only be created with declare -A.
Associative arrays were added to bash in version 4. If you're using an earlier version of bash, declare -A is not supported. You'll need to upgrade to a newer bash, code up some clumsy alternative, or use a language that does support associative arrays, like Awk, Python, or Perl.
Adding declare -A arr (as user000001's answer suggests) should solve the problem (if you have bash 4), but it's instructive to understand what your original code is actually doing (or rather not doing).
(BTW, thanks for asking this; I learned a lot as I was composing this answer.)
Declare the array variable as an associative array with declare -A arr.
$ cat test.sh
#!/bin/bash
set -x
declare -A arr
key="index.index";
arr["$key"]="val"
echo "${arr["${key}"]}"
$ ./test.sh
+ declare -A arr
+ key=index.index
+ arr["$key"]=val
+ echo val
val
This behavior varies by BASH version. Older BASH versions only allowed non negative integers as the keys in arrays.
Please note that dot/period is not allowed in a variable name in BASH.
See this Q&A for more details about allowed characters in BASH: Allowed characters in linux environment variable names
EDIT:
(Many thanks to #chepner for this addendum)
Regular arrays (not associative array) are indexed by integer only. Any expression used as an index between square brackets is treated as an arithmetic expression. $key expands to index, which is then treated as an (unset) variable which expands to 0. If you assigned a value of, say, 3 to index, then ${array[$key]} -> ${array[index]} -> ${array[3]}. It's a type of implicit indirect parameter expansion

How to store and echo multiple lines elegantly in bash? [duplicate]

This question already has answers here:
Capturing multiple line output into a Bash variable
(7 answers)
Closed 5 years ago.
I'm trying to capture a block of text into a variable, with newlines maintained, then echo it.
However, the newlines don't seemed to be maintained when I am either capturing the text or displaying it.
Any ideas regarding how I can accomplish this?
Example:
#!/bin/bash
read -d '' my_var <<"BLOCK"
this
is
a
test
BLOCK
echo $my_var
Output:
this is a test
Desired output:
this
is
a
test
You need to add " quotes around your variable.
echo "$my_var"

Resources