How do I expand variables in a bash variable without expanding wildcard? - bash

I have a variable that contains this kind of string :
var='$FOO/bar/baz*'
and I want to replace the variable $FOO by its content. However, when i do
var=$(eval "echo $var")
The variable is replaced, but the star is also replaced so that var now contains every possible match in my filesystem (as if i pressed tab in a shell).
for example, if $FOO contains /home, var will contain "/home/bar/baz1.sh /home/bar/baz2.sh /home/bar/baz.conf"
How do i replace the variable without expanding wildcards ?

Turn off globbing in bash, then reenable it.
set -f
var="$FOO/bar/baz*"
set +f

Just drop the quotes:
var=$FOO/bar/baz/*
Globs are not expanded on the RHS of a variable assignment.

Related

How to use double quotes when assigning variables?

There's a bash file with something like this:
FOO=${BAR:-"/some/path/with/$VAR/in/it"}
Are those double quotes necessary? Based on the following test, I'd say no, and that no quote at all is needed in the above assignment. In fact, it's the user of that variable that needs to expand it within double quotes to avoid wrong splitting.
touch 'some file' # create a file
VAR='some file' # create a variable for that file name
FOO=${BAR:-$VAR} # use it with the syntax above, but no quotes
ls -l "$FOO" # the file does exist (here we do need double quotes)
ls -l $FOO # without quotes it fails searching for files `some` and `file`
rm 'some file' # remove temporary file
Am I correct? Or there's something more?
Are those double quotes necessary?
Not in this case, no.
Am I correct?
Yes. And it's always the user of the variable that has to quote it - field splitting is run when expanding the variable, so when using it it has to be quoted.
There are exceptions, like case $var in and somevar1=$somevar2 - contexts which do not run field splitting, so like do not require quoting. But anyway, quotes do not hurt in such cases and can be used anyway.
Or there's something more?
From POSIX shell:
2.6.2 Parameter Expansion
In addition, a parameter expansion can be modified by using one of the following formats. In each case that a value of word is needed (based on the state of parameter, as described below), word shall be subjected to tilde expansion, parameter expansion, command substitution, and arithmetic expansion.
${parameter:-word}
Because field splitting expansion is not run over word inside ${parameter:-word}, indeed, quoting doesn't do much.

How to prevent evaluation of string in make / shell?

I have this makefile:
echo:
echo "PASS=$(PASS)"
Which I invoke:
PASS='MYPA$$' make
Which shows me:
echo "PASS=MYPA$"
PASS=MYPA$
Somebody is evaluating $$ -> $.
Is this the shell? Not when inputting the value, since I use single-quotes, preventing the shell to evaluate it.
Maybe the shell invoked by make is doing this ...
Or is it maybe make itself?
How can I avoid it?
On make variables
It's better to think of make variables as macros, than as conventional variables (actually in some versions of make, variables are called macros). The reason is, each time a variable is referenced, it is expanded.
An example from the docs illustrates the standard recursively expanded variables behaviour:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:
echo $(foo)
# echoes: Huh?
# `$(foo)' expands to `$(bar)' which expands to `$(ugh)' which finally expands to `Huh?'
If you are using GNU make, one way to avoid further expansion is by using the simply expanded variables:
Simply expanded variables are defined by lines using := (see section Setting Variables). The value of a simply expanded variable is scanned once and for all, expanding any references to other variables and functions, when the variable is defined. The actual value of the simply expanded variable is the result of expanding the text that you write. It does not contain any references to other variables; it contains their values as of the time this variable was defined.
Although simply expanded variables behave more like variables in most programming languages, their sole usage wouldn't solve the problem of environment variables' expansion here because even the first reference var := $(PASS) would expand $$ from the PASS environment variable.
Avoiding expansion of environment variables
We can use the shell function in make to read our environment variable in shell (and not expand it in make):
expanded := $(shell echo "$$PASS")
test:
echo 'PASS=$(expanded)'
echo "PASS=$$PASS"
The shell function will execute echo "$PASS" in shell ($$ is expanded to $ by make when function is executed), and the result (the value of your shell variable PASS) will be stored in the make variable expanded. This variable can now be freely used elsewhere in make, without ever being further expanded.
The only processing make does on the result, before substituting it into the surrounding text, is to convert each newline or carriage-return / newline pair to a single space. It also removes the trailing (carriage-return and) newline, if it's the last thing in the result.
The example above illustrates how to use the make variable expanded and the environment variable PASS in your Makefile script:
$ PASS='MYPA$$' make
echo 'PASS=MYPA$$'
PASS=MYPA$$
echo "PASS=$PASS"
PASS=MYPA$$

why doesn't bash IFS value split expansion argument?

>export FOOBAR=foobar; IFS=b echo ${FOOBAR}
I was expecting to see
foo ar
but I see
foobar
Why?
The IFS hasnt yet taken effect. add another ";":
FOOBAR=foobar IFS=b; echo ${FOOBAR}
In man bash section SIMPLE COMMAND EXPANSION
you can read (abbreviated):
When a simple command is executed
The words that the parser has marked as variable assignments (those preceding the command name) are saved for later processing.
The words that are not variable assignments or redirections are expanded.
...
The text after the = in each variable assignment ... [are] assigned to the variable.
so the IFS=b is done after expanding $FOOBAR.
[edit]I removed the technically incorrect answer.
http://tldp.org/LDP/abs/html/internalvariables.html
"This variable determines how Bash recognizes fields, or word boundaries, when it interprets character strings."

using double quotes in bash export statement

Hello I'm reading a book about bash scripting and the author says to add the following to the end of my .bashrc file. export PATH=~/bin:"$PATH" in order to execute my file from the command line by typing its name. I notice however that if I put export PATH=~/bin:$PATH I can achieve the same result. So my question is what is the difference between the one with quotes and the one without quotes? thanks.
The quotes won't hurt anything, but neither are they necessary. Assignments are processed specially by the shell. From the man page:
A variable may be assigned to by a statement of the form
name=[value]
If value is not given, the variable is assigned the null string. All values undergo tilde expansion, parameter and variable
expansion, command substitution, arithmetic expansion, and
quote removal (see EXPANSION below).
Notice that word-splitting and pathname generation are not on the list in bold. These are the two types of expansion you are trying to prevent by quoting a parameter expansion, but in this context they are not performed. The same rules apply to the assignments that are passed to the export built-in command.
You must include the variable PATH inside double quotes. So that it would handle the filepaths which has spaces but without double quotes, it won't handle the filenames which has spaces in it.
I was facing the same with trying to assign a JSON string to a variable in the terminal.
Wrap it with Single Quotes or Double Quotes
Use single quotes, if you string contains double quotes and vice-versa.
$ export TEMP_ENV='I like the "London" bridge'
$ echo $TEMP_ENV
>> I like the "London" bridge
$ export TEMP_ENV="I like the 'London' bridge"
$ echo $TEMP_ENV
>> I like the 'London' bridge

Theory: who can explain the use of =

can someone explain me with this code
data=$(date +"%Y-%m-%dS%H:%M:%S")
name="/home/cft/"$data"_test.tar"
touch $name
works, creating a new .tar file but this code doesn't work
data=$(date +"%Y-%m-%dS%H:%M:%S")
name= "/home/cft/"$data"_test.tar"
touch $name
and gives me this error: no such file or directory?
why the space between = and inverted commas creates this error?
Shell allows you to provide per-command environment overrides by prefixing the command with one or more variable assignments.
name= "/home/cft/"$data"_test.tar"
asks the shell to run the program named /home/cft/2013-10-08S12:00:00_test.tar (for example) with the value of name set to the empty string in its environment.
(In your case, the error occurs because the named tar file either doesn't exist or, if it does, is not an executable file.)
A variable assignment is identified by having no whitespace after the equal sign.
(name = whatever, of course, is simply a command called name with two string arguments, = and whatever.)
You can't have whitespace between the equal sign and the definition.
http://www.tldp.org/LDP/abs/html/varassignment.html
There is no theory behind this. It's just a decision the language designers made, and which the parser enforces.
In BASH (and other Bourne type shells like zsh and Kornshell), the equal sign cannot have spaces around it when setting variables.
Good:
$ foo="bar"
Bad:
$ foo= "bar"
$ foo = "bar"
There's no real reason that would prevent spaces from being used. Other programming languages have no problems with this. It's just the syntax of the shell itself.
The reason might be related to the original Bourne shell parsing where the shell would break up a command line based upon whitespace. That would make foo=bar a single parameter instead of two or three (depending if you have white space on both sides or just one side of the equal sign). The shell could see the = sign, and know this parameter is an assignment.
The shell parameter parsing is very primitive in many ways. Whitespace is very important. The shell has to be small and fast in order to be responsive. That means stripping down unessential things like complex line parsing.
Inverted commas I believe you mean quotation marks. Double quotes are used to override the breaking out of parameters over white space:
Bad:
$ foo=this is a test
bash: is: command not found
Good:
$ foo="this is a test"
Double quotes allow interpolation. Single quotes don't:
$ foo="bar"
$ echo "The value of foo is $foo"
The value of foo is bar
$ echo 'The value of foo is $foo'
The value of foo is $foo.
If you start out with single quotes, you can put double quotes inside. If you have single quotes, you can put double quotes inside.
$ foo="bar"
$ echo "The value of foo is '$foo'"
The value of foo is 'bar'
$ echo 'The value of foo is "$foo"'
The value of foo is "$foo"
This means you didn't have to unquote $data. However, you would have to put curly braces around it because underscores are legal characters in variable names. Thus, you want to make sure that the shell understand that the variable is $data and not $data_backup:
name="/home/cft/${data}_test.tar"

Resources