Getting bad substitution error in shell script - linux

I have a variable COUNTRY="INDIA"
and another sets of variables:-
INDIA_POPULATION="5,00,00,000", CHINA_POPULATION="6,00,00,000".
In script I am trying to call them using command:-
echo ${ ${COUNTRY}_POPULATION }
But I am getting bad substitution error. Can someone please tell how to solve it ??

You misplaced a bracket: {$COUNTRY}_POPULATION should be ${COUNTRY}_POPULATION. But even then it would not work as you expect. Use bash indirect expansion:
$ name="${COUNTRY}_POPULATION"
$ echo "${!name}"
5,00,00,000
Or, if you have bash version >= 4.3, you can also use a refname:
$ declare -n name="${COUNTRY}_POPULATION"
$ echo "$name"
5,00,00,000

Related

bash script variable concatenation

I have some bash code that i am running in a zsh shell on macos.
Code seems to work fine when i run in the shell, but when i run the file from my path, it only brings back the initial and not the initial+surname, any help most appreciated.
user="Steve Thomas"
dbuser=$user
initial="${user%"${user#?}"}"
userie=( $( echo $dbuser | cut -d' ' -f1- ) )
userlastname=${userie[2]}
fulluser="${initial}${userlastname}"
echo $fulluser
when run in shell i get what is expected
SThomas
and when i run as file.sh from path i get..
S
Not sure what i am doing wrong here, please advise.
In bash, arrays are indexed from 0, not 1 like in zsh. So, to make the script work in bash, change line 5 to
userlastname=${userie[1]}
You can even make it universal for both the shells:
startindex=2
if [[ $BASH_VERSION ]] ; then
startindex=1
fi
...
userlastname=${userie[startindex]}

What do three left angle brackets (`<<<`) mean in bash? [duplicate]

I'm getting this error
Syntax error: redirection unexpected
in the line:
if grep -q "^127.0.0." <<< "$RESULT"
How I can run this in Ubuntu?
<<< is a bash-specific redirection operator (so it's not specific to Ubuntu). The documentation refers to it as a "Here String", a variant of the "Here Document".
3.6.7 Here Strings
A variant of here documents, the format is:
<<< word
The word is expanded and supplied to the command on its
standard input.
A simple example:
$ cat <<< hello
hello
If you're getting an error, it's likely that you're executing the command using a shell other than bash. If you have #!/bin/sh at the top of your script, try changing it to #!/bin/bash.
If you try to use it with /bin/sh, it probably assumes the << refers to a "here document", and then sees an unexpected < after that, resulting in the "Syntax error: redirection unexpected" message that you're seeing.
zsh and ksh also support the <<< syntax.
if grep -q "^127.0.0." <<< "$RESULT"
then
echo IF-THEN
fi
is a Bash-specific thing. If you are using a different bourne-compatable shell, try:
if echo "$RESULT" | grep -q "^127.0.0."
then
echo IF-THEN
fi
It works for me on Ubuntu, if I complete you IF block:
if grep -q "^127.0.0." <<< "$RESULT"; then echo ""; fi

What's the point of eval/bash -c as opposed to just evaluating a variable?

Suppose you have the following command stored in a variable:
COMMAND='echo hello'
What's the difference between
$ eval "$COMMAND"
hello
$ bash -c "$COMMAND"
hello
$ $COMMAND
hello
? Why is the last version almost never used if it is shorter and (as far as I can see) does exactly the same thing?
The third form is not at all like the other two -- but to understand why, we need to go into the order of operations when bash in interpreting a command, and look at which of those are followed when each method is in use.
Bash Parsing Stages
Quote Processing
Splitting Into Commands
Special Operator Parsing
Expansions
Word Splitting
Globbing
Execution
Using eval "$string"
eval "$string" follows all the above steps starting from #1. Thus:
Literal quotes within the string become syntactic quotes
Special operators such as >() are processed
Expansions such as $foo are honored
Results of those expansions are split on characters into whitespace into separate words
Those words are expanded as globs if they parse as same and have available matches, and finally the command is executed.
Using sh -c "$string"
...performs the same as eval does, but in a new shell launched as a separate process; thus, changes to variable state, current directory, etc. will expire when this new process exits. (Note, too, that that new shell may be a different interpreter supporting a different language; ie. sh -c "foo" will not support the same syntax that bash, ksh, zsh, etc. do).
Using $string
...starts at step 5, "Word Splitting".
What does this mean?
Quotes are not honored.
printf '%s\n' "two words" will thus parse as printf %s\n "two words", as opposed to the usual/expected behavior of printf %s\n two words (with the quotes being consumed by the shell).
Splitting into multiple commands (on ;s, &s, or similar) does not take place.
Thus:
s='echo foo && echo bar'
$s
...will emit the following output:
foo && echo bar
...instead of the following, which would otherwise be expected:
foo
bar
Special operators and expansions are not honored.
No $(foo), no $foo, no <(foo), etc.
Redirections are not honored.
>foo or 2>&1 is just another word created by string-splitting, rather than a shell directive.
$ bash -c "$COMMAND"
This version starts up a new bash interpreter, runs the command, and then exits, returning control to the original shell. You don't need to be running bash at all in the first place to do this, you can start a bash interpreter from tcsh, for example. You might also do this from a bash script to start with a fresh environment or to keep from polluting your current environment.
EDIT:
As #CharlesDuffy points out starting a new bash shell in this way will clear shell variables but environment variables will be inherited by the spawned shell process.
Using eval causes the shell to parse your command twice. In the example you gave, executing $COMMAND directly or doing an eval are equivalent, but have a look at the answer here to get a more thorough idea of what eval is good (or bad) for.
There are at least times when they are different. Consider the following:
$ cmd="echo \$var"
$ var=hello
$ $cmd
$var
$ eval $cmd
hello
$ bash -c "$cmd"
$ var=world bash -c "$cmd"
world
which shows the different points at which variable expansion is performed. It's even more clear if we do set -x first
$ set -x
$ $cmd
+ echo '$var'
$var
$ eval $cmd
+ eval echo '$var'
++ echo hello
hello
$ bash -c "$cmd"
+ bash -c 'echo $var'
$ var=world bash -c "$cmd"
+ var=world
+ bash -c 'echo $var'
world
We can see here much of what Charles Duffy talks about in his excellent answer. For example, attempting to execute the variable directly prints $var because parameter expansion and those earlier steps had already been done, and so we don't get the value of var, as we do with eval.
The bash -c option only inherits exported variables from the parent shell, and since I didn't export var it's not available to the new shell.

"exec not found" when using variable in exec

I met a strange problem.
mkfifo "spch2008"
exec 100<>"spch2008"
It's OK. But, when i use variable to replace "100", error occurs.
exec: 100: not found
PIPE_ID=100
mkfifo "spch2008"
exec ${PIPE_ID}<>"spch2008"
I don't know the reason. please hlep me,thanks.
It is caused by shell not performing variable expansion on the left side of the redirection operator. You can use a workaround:
eval exec "${PIPE_ID}"'<>"spch2008"'
It will force the shell to do variable expansion, producing
eval exec 100'<>"spch2008"'
Then the eval built-in will feed the command to the shell, which will effectively execute
exec 100<>"spch2008"
I/O redirection doesn't allow variables to specify the file descriptors generally, not just in the context of the <> redirection.
Consider:
$ cat > errmsg # Create script
echo "$#" >&2 # Echo arguments to standard error
$ chmod +x errmsg # Make it executable
$ x=2
$ ./errmsg Hi # Writing on standard error
Hi
$ ./errmsg Hi ${x}>&1 # Writing on standard error
Hi 2
$ ./errmsg Hi 2>&1 # Redirect standard error to standard output
Hi
$ ./errmsg Hi 2>/dev/null # Standard error to /dev/null
$ ./errmsg Hi ${x}>/dev/null # Standard output to /dev/null
Hi 2
$

save wild-card in variable in shell script and evaluate/expand them at runtime

I am having trouble running the script below (in Cygwin on win 7 mind you).
Lets call it "myscript.sh"
When I run it, the following is what I input:
yearmonth: 2011-03
daypattern: 2{5,6,7}
logfilename: error*
query: WARN
#! /bin/bash
yearmonth=''
daypattern=''
logfilename=''
sPath=''
q=''
echo -n "yearmonth: "
read yearmonth
echo -n "daypattern: "
read daypattern
echo -n "logfilename: "
read logfilename
echo -n "query: "
read q
cat "$yearmonth/$daypattern/$logfilename" | grep --color $q
The output I get is:
cat: /2011-03/2{5,6,7}/error* No such
directory of file exists.
However, if I enter daypattern=25 OR daypattern=26 etc. the script will work.
Also, of course if I type the command in the shell itself, the wildcards are expanded as expected.
But this is not what I want.
I want to be able to PROMPT the user to enter the expressions as they need, and then later, in the script, execute these commands.
Any ideas how this can be possible?
Your help is much appreciated.
Try eval, this should work for the {a,d} and * cases
eval grep --color $q ${yearmonth}/${daypattern}/${logfilename}
Use quote to prevent wildcard expansion:
$ a="*.py"
$ echo $a
google.py pair.py recipe-523047-1.py
$ echo "$a"
*.py

Resources