How to pass string variable to function in shell? [duplicate] - linux

This question already has answers here:
Inserting “local” before variable name in a shell script leads to an error
(3 answers)
Closed 1 year ago.
I have method that should receive string, but it does not work as intended
receive_string()
{
local string=$1
echo "$string"
}
When I call it I get unrelated error.
receive_string "Catch a string my friend!"
Returns:
4: local: friend!: bad variable name
Instead of
Catch a string my friend!
What is the problem, and how to solve it?

The problem is that variable expansion is literal in bash, so
local string=$1 will behave as local string=Catch a string my friend!.
It is a good practice to enclose between double quotes your string variables, so they don't separate during expansion. Try this:
local string="$1"
It won't add quotes to your string and it will keep it together.
About the ! specific behavior, check KamilCuk's answer.

You are not running your script with bash. You are running your script under dash shell. The behavior does not happen on bash - in bash the command local is very specially handled like ex. export and arguments have same semantics as on assignment. Most probably the shebang of your script is #!/bin/sh and sh is linked to dash on your system. Use shebang with bash to run bash.
local string=$1
is expanding $1 so it becomes:
local string=Catch a string my friend!
which creates a variable string with value Catch, empty variables a string and my and friend! is invalid variable value.
As always, quote variable expansion.
local string="$1"
Research when to quote variables in shell. Check your scripts with http://shellcheck.net
Side note: the ! in "something!" triggers history expansion in bash. In bash when in interactive shell with history expansion enabled, you would put it ex. in single quotes "something"'!'.

Related

Why do quotes in shell scripts behave differently from quotes in shell commands?

I'm using WSL (Ubuntu 18.04) on Windows 10 and bash.
I have a file filename.gpg with the content:
export SOME_ENV_VAR='123'
Now I run the following commands:
$ $(gpg -d filename.gpg)
$ echo $SOME_ENV_VAR
'123' <-- with quotes
However, if I run it directly in the shell:
$ export SOME_ENV_VAR='123'
$ echo $SOME_ENV_VAR
123 < -- without quotes
Why does it behave like this? Why is there a difference between running a command using $() and running it directly?
Aside: I got it working using eval $(gpg -d filename), I have no idea why this works.
Quotes in shell scripts do not behave differently from quotes in shell commands.
With the $(gpg -d filename.gpg) syntax, you are not executing a shell script, but a regular single command.
What your command does
It executes gpg -d filename.gpg
From the result, it takes the first (IFS-separated) word as the command to execute
It takes every other (IFS-separated) words, including words from additional lines, as its parameters
It executs the command
From the following practical examples, you can see how it differs from executing a shell script:
Remove the word export from filename.gpg: the command is then SOME_ENV_VAR='123' which is not understood as a variable assignment (you will get SOME_ENV_VAR='123': command not found).
If you add several lines, they won't be understood as separated command lines, but as parameters to the very first command (export).
If you change export SOME_ENV_VAR='123' to export SOME_ENV_VAR=$PWD, SOME_ENV_VAR will not contain the content of variable PWD, but the string $var
Why is it so?
See how bash performs expansion when analyzing a command.
There are many steps. $(...) is called "command substitution" and is the fourth step. When it is done, none of the previous steps will be performed again. This explains why your command does not work when you remove the export word, and why variables are not substituted in the result.
Moreover "quote Removal" is the last step and the manual reads:
all unquoted occurrences of the characters ‘\’, ‘'’, and ‘"’ that did
not result from one of the above expansions are removed
Since the single quotes resulted from the "command substitution" expansion, they were not removed. That's why the content of SOME_ENV_VAR is '123' and not 123.
Why does eval work?
Because eval triggers another complete parsing of its parameters. The whole set of expansions is run again.
From the manual:
The arguments are concatenated together into a single command, which is then read and executed
Note that this means that you are still running one single command, and not a shell script. If your filename.gpg script has several lines, subsequent lines will be added to the argument list of the first (and only) command.
What should I do then?
Just use source along with process substitution.
source <(gpg -d filename.gpg)
Contrary to eval, source is used to execute a shell script in the current context. Process substitution provides a pseudo-filename that contains the result of the substitution (i.e. the output of gpg).

How can I escape arguments passed in bash script command line

I have one variable, which is coming from some where like:
VAR1='hhgfhfghhgf"";2Ddgfsaj!!!$#^$\'&%*%~*)_)(_{}||\\/'
Now i have command like this
./myscript.sh '$VAR1'
I am getting that $VAR1 from some diff process and when I display it look exactly as its above.
Now that command is failing as there is already single quote inside variable. In the process where I use it it is expanded at that point, which causes that error.
I have control over myscript.sh but not above command.
Is there any way I can get variable inside my script?
What you are saying is not possible to failing when passing to your script. Might your script has processing issue (or a command where this argument will passing into it) which cannot expand the variable correctly. You can either use printf with %q modifier to escape all special characters then pass it to your script:
./myscript.sh "$(printf '%q\n' "$VAR1")"
... or do the same within your script before you wanted to pass to some other commands:
VAR2="$(printf '%q\n' "$VAR1")"

Edit remote file with variables

I've write this script but it does not works:
E_OPT=" some_host(ro,insecure) some_host2(ro,insecure)"
echo -n "Insert path to export [ ex: /path/test ]"
read PATH
FINAL=$PATH$E_OPT
ssh SERVER echo "$FINAL" >> file
or
ssh SERVER echo '$FINAL >> file'
or
ssh SERVER 'echo "$FINAL" >> file'
How can I pass text in variable to append in remote files?
There are a couple of problems here. The first is with read PATH. The variable PATH is one of many that have special meaning to the system: it defines where to look for executables (e.g. for commands). As soon as you redefine it as something else, the system will be unable to find executables like ssh, so commands will start to fail. Solution: use lowercase or mixed-case variable names to avoid conflicts with any of the special-meaning variables (which are all uppercase).
Second, all of your attempts at quoting are wrong. The command is going to go through two levels of shell parsing: first on the local computer (where you want the variable $FINAL -- or better $final -- to be expanded), and then on the remote server (where you want the parentheses to be in quotes, so they don't cause shell syntax errors). This means you need two levels of quoting: an outer leven that gets parsed & removed by the local shell, and a second level that gets parsed & removed by the remote shell. Variable expansion is only done in double-quotes, not single-quotes, so the outer level has to be double-quotes. The inner level could be either, but single-quotes are going to be easiest:
ssh SERVER "echo '$final' >> file"
Now, it may look like that $final variable is in single-quotes so it won't get expanded; but quotes don't nest! As far as the local shell is concerned, that's a double-quoted string that happens to contain some single-quotes or apostrophes or something that doesn't really matter. When the remote shell receives the command, the variable has been substituted and the outer quotes removed, so it looks like this:
echo '/some/path some_host(ro,insecure) some_host2(ro,insecure)' >> file
...which is what you want.
You must export the FINAL variable, besides, you also need to execute your script with a dot at the beginning, like:
. server-script.sh
This will evaluate the variables on the local bash, instead of a sub-shell.

BASH script with a $ not saving correctly [duplicate]

This question already has answers here:
How to echo a variable containing an unescaped dollar sign in bash
(4 answers)
Closed 6 years ago.
I have a bash script, which I run doing the following sudo ./test
The bash script needs to create a repo, and save the following data in it.
So, this is the bash script:
#!/bin/bash
echo "Inputing data... "
echo "[mongodb-org-3.2]
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/" > /etc/yum.repos.d/mongodb-org.repo
However, because the releasever has a $ in front of it, the script save the text like this:
[mongodb-org-3.2]
baseurl=https://repo.mongodb.org/yum/redhat//mongodb-org/3.2/x86_64/
Instead of like this:
[mongodb-org-3.2]
baseurl=https://repo.mongodb.org/yum/redhat/$c/mongodb-org/3.2/x86_64/
Any idea how I can treat the $releasever as text rather than a variable?
I tried putting double quotes around it however that still does not work. I am new to bash scripting so any help appreciated.
Thanks!
Use single quotes to prevent variable expansion.
Using double quotes:
sh-4.1$ baseurl1="https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/"
sh-4.1$ echo $baseurl1
https://repo.mongodb.org/yum/redhat//mongodb-org/3.2/x86_64/
Using single quotes:
sh-4.1$ baseurl2='https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/'
sh-4.1$ echo $baseurl2
https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/
Use the back-slash character to indicate that you don't want to call a bash variable, e.g.:
echo \$HELLO

Bash script passing string argument containing $

I need to pass a string argument to a bash script that may contain a $ character. I don't want to force a \ to be inserted into the string outside of the script.
I tried to do that within the script, but couldn't figure out how to do this.
I had a similar issue at a later point in the script where I read in a string using "read". I could only get it to work by forcing the user to enter \$, which is not going to work for my application.
Any suggestions ?
If you don't want to have to escape the $ with a backslash, then the only other alternative is to surround the argument in single quotes. It's not possible to pass a 'naked' $ into your script because the shell will try to expand it. Using single quotes prevents shell expansion and will preserve the $.
For example:
myscript.sh '$foo'

Resources