setting variable with unknown name in bash - bash

I would like to set a variable which name is stored in a file (is an output of a sed executed earlier)
the file would look like:
py1
so setting our variable would be like: set cat file=value
but echoing $py1 gives me nothing.
Is that possible with bash version 2.05?

This is the "preferred" way to do it (bash) without using cat or eval
declare $(<file)=value

Use eval:
eval "$(cat file)=value"
Update: The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).

You'll need to use eval
eval $(cat file)=value

Related

bash not recognizing new lines in multiline variable assignment [duplicate]

In Bash (or other shells) how can I print an environment variable which has a multi-line value?
text='line1
line2'
I know a simple usual echo $text won't work out of the box.
Would some $IFS tweak help?
My current workaround is something like ruby -e 'print ENV["text"]'.
Can this be done in pure shell? I was wondering if env command would take an unresolved var name but it does not seem to.
Same solution as always.
echo "$text"
export TEST="A\nB\nC"
echo $TEST
gives output:
A\nB\nC
but:
echo -e $TEST
A
B
C
So, the answer seems to be the '-e' parameter to echo, assuming that I understand your question correctly.

How to expand a variable "read" from terminal

For example,
create a bash tmp.sh script with the following,
export tmp=abc
read _test
echo "$_test"
Execute bash tmp.sh
Input '$tmp/def'.
Expected result: 'abc/def'
Actual result: '$tmp/def'
check this
eval "echo $_test"
or
bash -c "echo $_test"
Edit Latter (bash -c) uses sub-shell which is safe in comparison with eval
You can use the envsubst command to substitute environment variables like this:
echo "$_test" | envsubst
or, since this is in bash:
envsubst <<<"$_test"
This is significantly safer than either eval or bash -c, since it won't do anything other than replacing instances of $var or ${var} with the corresponding variable values.

How to set variables whose name and values are taken from a command's output with `declare` in bash?

I need to declare variables in a bash shell script for which both names and values are taken from another command's output.
For the sake of this question, I will use a temporary file tmp:
$ cat tmp
var1="hello world"
var2="1"
... and use it for my mock command below.
In the end, I need to have the variables $var1 and $var2 set with respectively hello world and 1, with the variable names var1 and var2 taken directly from the input.
Here is what I got so far:
$ cat tmp|while read line; do declare $line; done
I know I don't need to use catbut this is to simulate the fact that the input is taken from the output of an other command and not in a file.
This doesn't work. I get:
bash: declare: `world"': not a valid identifier
and
$ echo $var1; echo $var2
$
I don't understand why this doesn't work since I can do this:
declare var1="hello world"
... with expected result. I assumed this would be equivalent, but I'm clearly wrong.
I found this answer as the closest thing to my problem, but not quite since it relies on a file to source. I would like to avoid that. I found other answers that uses eval but I'd prefer to avoid that as well.
Maybe there are subtleties in the use of quotes I don't understand.
If the only way is to use a temporary file and source it that is what I'll do, but I think there must be another way.
A good suggestion when writing a shell script is that always double quoting the variable. Otherwise, it will be affected by the shell word splitting.
while read line; do
declare "$line"
done < <(echo "var1=hello world")
And why echo "var1=hello world" | while read line; do export "$line"; done won't work? Because pipe is a sub-shell, it creates var1 in the sub-shell, it won't impact the current shell. So it can't be set in the current shell.
As an alternative, use process substitution, you can obtain the output as a temporary file. So it will create the variable in the current shell.

fgrep with string containing spaces inside ksh script

I am trying to write an fgrep statement removing records with a full record match from a file. I can do this on the command line, but not inside a ksh script. The code I am using boils down to these 4 lines of code:
Header='abc def|ghi jkl' #I use the head command to populate this variable
workfile=abc.txt
command="fgrep -Fxv \'$Header\' $workfile" >$outfile
$command
When I echo $command to STDIN the command is exactly what I would type on the command line (with the single quotes) and that works on the command line. When I execute it within the ksh script (file) the single quotes seem not to be recognized because the errors show it is parsing on spaces.
I have tried back ticks, exec, eval, double quotes instead of single quotes, and not using the $command variable. The problem remains.
I can do this on the command line, but not inside a ksh script
Here's a simple, portable, reliable solution using a heredoc.
#!/usr/bin/env ksh
workfile=abc.txt
outfile=out.txt
IFS= read -r Header <<'EOF'
abc def|ghi jul
EOF
IFS= read -r command <<'EOF'
grep -Fxv "$Header" "$workfile" > "$outfile"
EOF
eval "$command"
Explanation :
(Comments can't be added to the script above because they would affect the lines in the heredoc)
IFS= read -r Header <<'EOF' # Line separated literal strings
abc def|ghi jul # Set into the $Header variable
EOF # As if it were a text file
IFS= read -r command <<'EOF' # Command to execute
grep -Fxv "$Header" "$workfile" > "$outfile" # As if it were typed into
EOF # the shell command line
eval "$command" # Execute the command
The above example is the same as having a text file called header.txt, which contains the contents: abc def|ghi jul and typing the following command:
grep -Fxvf header.txt abc.txt
The heredoc addresses the problem of the script operating differently than the command line as a result of quoting/expansions/escaping issues.
A Word of caution regarding eval:
The use of eval in this example is specific. Please see Eval command and security issues for information on how eval can be misused and cause potentially very damaging results.
More Detail / Alternate Example:
For the sake of completeness, clarity, and ability to apply this concept to other situations, some notes about the heredoc and an alternative demonstration:
This implementation of the heredoc in this example is specifically designed with the following criteria:
Literal string assignment of contents, to the variables (using 'EOF')
Use of the eval command to evaluate and execute the referenced variables within the heredoc itself.
File or heredoc ?
One strength of using a heredoc combined with grep -F (fgrep), is the ability to treat a section of the script as if it were a file.
Case for file:
You want to frequently paste "pattern" lines into the file, and remove them as necessary, without having to modify the script file.
Case for heredoc:
You apply the script in an environment where specific files already exist, and you want to match specific exact literal patterns against it.
Example:
Scenario: I have 5 VPS Servers, and I want a script to produce a new fstab file but to ensure it doesn't contain the exact line:
/dev/xvda1 / ext3 errors=remount-ro,noatime,barrier=0 0 1
This scenario fits the type of situation addressed in this question. I could use the boilerplate from the above code in this answer and modify it as following:
#!/usr/bin/env ksh
workfile=/etc/fstab
IFS= read -r Header <<'EOF'
/dev/xvda1 / ext3 errors=remount-ro,noatime,barrier=0 0 1
EOF
IFS= read -r command <<'EOF'
grep -Fxv "$Header" "$workfile"
EOF
eval "$command"
This would give me a new fstab file, without the line contained in the heredoc.
Bash FAQ #50: I'm trying to put a command in a variable, but the complex cases always fail! provides comprehensive guidance - while it is written for Bash, most of it applies to Ksh as well.[1]
If you want to stick with storing your command in a variable (defining a function is the better choice), use an array, which bypasses the quoting issues:
#!/usr/bin/env ksh
Header='abc def|ghi jkl'
workfile=abc.txt
# Store command and arguments as elements of an array
command=( 'fgrep' '-Fxv' "$Header" "$workfile" )
# Invoke the array as a command.
"${command[#]}" > "$outfile"
Note: only a simple command can be stored in an array, and redirections can't be part of it.
[1] The function examples use local to create local variables, which ksh doesn't support. Omit local to make do with shell-global variables instead, or use function <name> {...} syntax with typeset instead of local to declare local variables in ksh.

Print a variable with multi-line value in shell?

In Bash (or other shells) how can I print an environment variable which has a multi-line value?
text='line1
line2'
I know a simple usual echo $text won't work out of the box.
Would some $IFS tweak help?
My current workaround is something like ruby -e 'print ENV["text"]'.
Can this be done in pure shell? I was wondering if env command would take an unresolved var name but it does not seem to.
Same solution as always.
echo "$text"
export TEST="A\nB\nC"
echo $TEST
gives output:
A\nB\nC
but:
echo -e $TEST
A
B
C
So, the answer seems to be the '-e' parameter to echo, assuming that I understand your question correctly.

Resources