create variables inside unix script - linux

I am trying to create a variable in a script, based on another variable.
I just don't know what needs to be adjusted in my code, if it is possible.
I am simplifying my code for your understanding, so this is not the original code.
The code goes like that:
#!/bin/csh -f
set list_names=(Albert Bela Corine David)
set Albert_house_number=1
set Bela_house_number=2
set Corine_house_number=3
set David_house_number=4
foreach name ($list_names)
#following line does not work....
set house_number=$($name\_house_number)
echo $house_number
end
the desired output should be:
1
2
3
4
Thanks for your help.

Unfortunately, the bashism ${!varname} is not available to us in csh, so we'll have to go the old-fashioned route using backticks and eval. csh's quoting rules are different from those of POSIX-conforming shells, so all of this is csh specific. Here we go:
set house_number = `eval echo \" \$${name}_house_number\"`
echo "$house_number"
${name} is expanded into the backticked command, so this becomes equivalent to, say,
set house_number = `eval echo \" \$Albert_house_number\"`
which then evaluates
echo " $Albert_house_number"
and because of the backticks, the output of that is then assigned to house_number.
The space before \$$ is necessary in case the value of the expanded variable has special meaning to echo (such as -n). We could not simply use echo "-n" (it wouldn't print anything), but echo " -n" is fine.1
The extra space is stripped by csh when the backtick expression is expanded. This leads us to the remaining caveat: Spaces in variable values are going to be stripped; csh's backticks do that. This means that if Albert_house_number were defined as
set Albert_house_number = "3 4"
house_number would end up with the value 3 4 (with only one space). I don't know a way to prevent this.
1 Note that in this case, the echo "$house_number" line would have to be amended as well, or it would run echo "-n" and not print anything even though house_number has the correct value.

Related

Indirect expansion returns variable name instead of value

I am trying to set up some variables using indirect expansion. According to the documentation I've read, the set up should be simple:
var1=qa
qa_num=12345
varname="${var1}_ci"
echo ${!varname}
I should be getting "12345". Instead, the output is "varname". If I remove the exclamation point, I end up with "qa_ci", not "12345"
This should be a relatively simple solution, so I'm not sure what I'm missing, if anything.
Your code defines qa_num, but the varname assignment references qa_ci. As a result, your echo was expanding nonexistent qa_ci, giving empty results. Changing the varname assignment fixes the problem on my system.
Example: foo.sh:
#!/bin/bash
var1=qa
qa_num=12345
varname="${var1}_num" # <=== not _ci
echo "${!varname}" # I also added "" here as a general good practice
Output:
$ bash foo.sh
12345

Simulating ENTER key and export another variable together in Linux bash

I should put ENTER key value as a shell input to avoid shell waiting for user input.
echo 'enter'
read FRUIT
case "$FRUIT" in
"apple") echo "Apple pie is quite tasty."
;;
"banana") echo "I like banana nut bread."
;;
"kiwi") echo "New Zealand is famous for kiwi."
;;
esac
export TEST6=TEST
I have learned that 'echo' implicitly has an ENTER value, so tried to run below command to accomplish my requirement.
echo | source ~/.bash_profile
As I expected, I can see the OS prompt instead of seeing Shell wait the user input.
However, I noticed that the last line exporting TEST6 variable doesn't get exported. I mean I can't find the value of the variable TEST6.
Can you let me know what I am missing?
The value is being exported in a subshell run by the pipeline. Use input redirection instead.
source ~/.bash_profile <<< ""
Any string could be used, since you don't appear to care about the actual value used, but an empty string is the equivalent of what echo with no arguments produces. (All here strings implicitly end with a newline.)
If your shell does not support <<<, you can use a POSIX-standard here document instead.
. ~/some_file <<EOF
EOF

linux shell - strange 'if'?

I am reading a source code, and find these lines :
if [ -n "${INIT_NAMENODE+1}" ]
then
echo "Initializing namenode"
else
echo "Starting namenode"
fi
how should I interpred the 'if' condition : if [ -n "${INIT_NAMENODE+1}" ] ? ?
The nice thing about this code is that it is not written for a "Linux shell". It is written for the more general category of "UNIX shell". It will work in everything since V7 UNIX (1979) at least. People with lesser portability goals might write it without the -n.
The first item of interest is the ${foo+bar} syntax. This is a test for existence of the foo parameter. If $foo exists, then ${foo+bar} equals bar. If $foo doesn't exist, then ${foo+bar} equals the empty string.
If you look for this in your shell man page, it's usually documented as ${foo:+bar}, along with some other related forms like ${foo:-bar}, and somewhere nearby there's a note explaining that the colon can be omitted from all of them, resulting in slightly different behavior (with the colon, variables whose value is the empty string are treated the same as nonexistent variables).
Next we have the [ -n ... ] test. -n tests the following string for emptiness. It succeeds if the string is non-empty. From the previous paragraph we know that ${INIT_NAMENODE+1} is empty if and only if $INIT_NAMENODE doesn't exist. So the -n test succeeds if $INIT_NAMENODE exists. The value 1 doesn't really matter here - it would do the same thing if you changed the 1 to 2 or 0 or teapot. All that matters is that it's not an empty string, since -n doesn't care about the rest.
Try some examples from your shell prompt: echo ${PATH+hello} should say hello because you do have a $PATH variable. echo ${asdfghjkl+hello} should print a blank line.
So, in the context of the if statement, the purpose of the test is to do the first echo if the variable $INIT_NAMENODE exists, and the second echo if the variable $INIT_NAMENODE doesn't exist.

Why doesnt command execute successfully if i use variables?

I have the following script
WSO2_SCRIPT="JAVA_HOME=$JAVA_HOME /opt/autopilot/wso2is/bin/wso2server.sh"
WSO2_LOG="/var/log/autopilot/wso2is/autopilot-wso2is-initd.log"
${WSO2_SCRIPT} start >> ${WSO2_LOG} 2>&1 || echo failed
JAVA_HOME=$JAVA_HOME /opt/autopilot/wso2is/bin/wso2server.sh start >> /var/log/autopilot/wso2is/autopilot-wso2is-initd.log 2>&1 || echo failedagain
The third line of the code results in failure as I have "failed" echoed?
However the fourth line is successful and I don't get "failedagain" echoed.
Line 3 and 4 should result in exactly the same thing. Only difference is I am using variables in in line 3, and being explicit in line 4.
Why does using variables result in a failure?
When variables are expanded without quotes, they undergo word splitting and pathname expansion, but not shell grammar parsing.
This means that you can put the following in variables:
Multiple arguments (including command name) to be split up on spaces and spaces only
Globs like *.txt to be expanded
It also means that you can not put anything of the following in variables:
Redirections
Quotes
Pipes
Backgrounding &
Conditionals and loops, including if and [[ .. ]]
Brace and parenthesis groups
Parameter expansion
Command substitutions
Process substitutions
and as you've discovered: variable assignments
If you want to pass any of the above around as a variable, you should use a function and refer to the function instead. If you don't care about security and good practice, you can also use eval to evaluate a text string as shell code.

Bash variable defaulting doesn't work if followed by pipe (bash bug?)

I've just discovered a strange behaviour in bash that I don't understand. The expression
${variable:=default}
sets variable to the value default if it isn't already set. Consider the following examples:
#!/bin/bash
file ${foo:=$1}
echo "foo >$foo<"
file ${bar:=$1} | cat
echo "bar >$bar<"
The output is:
$ ./test myfile.txt
myfile.txt: ASCII text
foo >myfile.txt<
myfile.txt: ASCII text
bar ><
You will notice that the variable foo is assigned the value of $1 but the variable bar is not, even though the result of its defaulting is presented to the file command.
If you remove the innocuous pipe into cat from line 4 and re-run it, then it both foo and bar get set to the value of $1
Am I missing somehting here, or is this potentially a bash bug?
(GNU bash, version 4.3.30)
In second case file is a pipe member and runs as every pipe member in its own shell. When file with its subshell ends, $b with its new value from $1 no longer exists.
Workaround:
#!/bin/bash
file ${foo:=$1}
echo "foo >$foo<"
: "${bar:=$1}" # Parameter Expansion before subshell
file $bar | cat
echo "bar >$bar<"
It's not a bug. Parameter expansion happens when the command is evaluated, not parsed, but a command that is part of a pipeline is not evaluated until the new process has been started. Changing this, aside from likely breaking some existing code, would require extra level of expansion before evaluation occurs.
A hypothetical bash session:
> foo=5
> bar='$foo'
> echo "$bar"
$foo
# $bar expands to '$foo' before the subshell is created, but then `$foo` expands to 5
# during the "normal" round of parameter expansion.
> echo "$bar" | cat
5
To avoid that, bash would need some way of marking pieces of text that result from the new first round of pre-evaluation parameter expansion, so that they do not undergo a second
round of evaluation. This type of bookkeeping would quickly lead to unmaintainable code as more corner cases are found to be handled. Far simpler is to just accept that parameter expansions will be deferred until after the subshell starts.
The other alternative is to allow each component to run in the current shell, something that is allowed by the POSIX standard, but is not required, either. bash made the choice long ago to execute each component in a subshell, and reversing that would break too much existing code that relies on the current behavior. (bash 4.2 did introduce the lastpipe option, allowing the last component of a pipeline to execute in the current shell if explicitly enabled.)

Resources