I am trying to read a file using Make's $(file) function. The documentation suggests something like:
MYVAR = $(file < foo)
should work since:
When reading from a file, the file function expands to the verbatim contents of the file, except that the final newline (if there is one) will be stripped. Attempting to read from a non-existent file expands to the empty string.
But the above code just throws an "invalid file operation: < foo" error.
Related
I have the template file 12A-r.inp . I want to prepare files from this file whose name will be 16A-r.inp, 20A-r.inp, 24A-r.inp. And I want to change some parameter in those files according to their names. For example, I want to replace the string "12A" in all places in file 12A-r.inp, with 16A in 16A-r.inp, and 20A in 20A-r.inp. I have written the code below for this:
for ((i=12;i<=24;i=i+4))
do
cat 12A-r.inp >> $i\A-r.inp
done
for ((i=12;i<=24;i=i+4))
do
sed -i "s/12A/${i}/g" $i\A-r.inp
done
But the problem is 12A gets replaced by ${i}, not with strings like 16A, 20A etc.
Observations:
In for ((i=12;i<=24;i=i+4)) counts 12,16,20,24. There's no need
to start at 12, since the template is already correct. Worse,
when i=12, this code cat 12A-r.inp >> $i\A-r.inp appends a
copy of the template file onto itself, doubling it, which causes every ensuing
created file to be twice as long as the original template.
The \ in $i\A-r.inp is unnecessary, since A is not a special character.
The cat is unnecessary, sed without -i can do it all.
In sed, s/12A/${i}/g would replace the string "12A", with whatever number $i is, without the "A", unless the variable includes that letter.
The for loop uses a bashism to enumerate i... in this instance there's a simpler equivalent bashism, (see below).
Suggested revision:
for i in {16..24..4}A
do
sed "s/12A/${i}/g" 12A-r.inp > ${i}-r.inp
done
How it works:
$i is set to 16A,20A, and 24A.
sed repeatedly reads in the template, replaces 12A with $i,
prints everything to STDOUT...
which is redirected to the appropriately named file.
Im trying to create an array and then get the value of a key using the following commands:
declare -A email_addresses
mail_address=(["dev"]="dev.com" ["sandbox"]="sandbox.com")
env=$(#command to get env) # result is "sandbox"
echo ${email_address[$env]}
However it keeps throwing this error at me : -bash: "hsandbox": syntax error: operand expected (error token is ""sandbox"")
Im not sure how to get past this. If I do echo $env it returns "sandbox" and not ""sandbox"" so Im not sure what seems to be the issue.
Fix your "command to get env" to not be emitting literal quotes in its output. Barring that:
# strip leading and trailing quotes from env
env=${env%'"'}; env=${env#'"'}
echo "${email_address[$env]}"
Python-Audience-Friendly Explanation
To explain this in a manner that makes sense to folks who know Python (since that's where most of the OP's rep comes from):
echo "$foo" in shell behaves like the Python command print str(foo), not the Python command print repr(foo).
Consider the following REPL session:
>>> mail_address = { "dev": "dev.com", "sandbox": "sandbox.com" }
>>> env = getSomething()
>>> print str(env)
"dev"
>>> print mail_address[env]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: '"dev"'
>>> print repr(env)
'"dev"'
You've got the exact same problem: Your dictionary contains dev as its literal contents, but your key's literal contents are "dev".
Avoiding Confusion In The Future: Unambiguously Printing Shell Variables
If you want to print a variable's contents in shell in a way that's unambiguous (in the same respect in which print repr(env) is unambiguous in Python), echo is the wrong tool for the job. Consider instead one of the following:
$ declare -p env ## caveat: doesn't work for all non-printable characters
declare -- env="\"dev\""
$ printf 'env=%q\n' "$env" ## caveat: doesn't work for non-string datatypes
env=\"dev\"
An Aside: Why You Should Always Quote Arguments To echo (Or Not Use It)
While it looks innocuous, the code
echo $foo
actually has surprisingly complicated behavior. Consider the following:
foo=$'\thello\tworld\t*\n\\text'
That's the bash equivalent to the following Python:
foo='\thello\tworld\t*\n\\text'
Now, let's see what happens if you actually use echo to print it with echo $foo, if you have a default value for IFS and your shell is bash:
The first tab disappears altogether
The other tabs are replaced by spaces
The newline literal is replaced by a space
The * is replaced by a list of files in the current directory.
That is to say, the behavior in bash of echo $foo is equivalent to the following Python:
import itertools, glob
foo='\thello\tworld\t*\n\\text'
print ' '.join(itertools.chain(*[ glob.glob(s) for s in foo.split() ]))
By contrast, consider:
echo "$foo"
In that case, you'll get the expected behavior... in bash.
Why "in bash"? Because the POSIX standard for echo doesn't specify behavior when any backslash literal is included in the text. echo could do literally anything in this circumstance and still be POSIX-compliant, and BSD-style implementations will behave differently than XSI-style ones do.
I am trying to write a Makefile for GNU make. I can't figure out what the problem is here:
foo := this|works
bar := "I lost my 'single quotes'"
baz := 'make|will|not|accept|this|with|the|single|quotes'
whatIWant := "A string with its 'single|quoted|regex|alternatives'"
this-almost-works: #But the single quotes are lost.
#printf '%s ' "$(whatIWant)"
this-fails-horribly:
#printf '$(whatIWant)'
I get the following error message
/bin/sh: 1: quoted: not found
/bin/sh: 1: /bin/sh: 1: regex: not foundalternatives": not found
blah blah Error 127
Why is it trying to run parts of this string in the shell?
How can I define a variable to contain exactly the contents of whatIWant?
Might be worth looking in detail at the expansion.
When defining variables,
just about the only character that has an effect is $.
Everything else is taken literally.
It's worth nothing that white space around the assignment operator (= or :=) is ignored.
foo := this|works
foo is assigned the literal text this|works.
Similarly,
baz := 'make|will|not|accept|this|with|the|single|quotes'
assigns the literal text 'make|will|not|accept|this|with|the|single|quotes' to baz.
Fine and dandy.
Now, when make decides to build this-fails-horribly
(possibly because you said to the shell make this-fails-horribly)
it expands the block of commands before doing anything.
Not unreasonably,
$(whatIWant) is replaced by "A string with its 'single|quoted|regex|alternatives'".
Again, fine and dandy.
What is left is passed verbatim, one line at a time, to the shell.
The shell sees
printf '"A string with its 'single|quoted|regex|alternatives'"'
(which make would have helpfully echoed to you if you had left off the # prefix).
Now we are in the land of shell quoting.
The printf command is passed one parameter: "A string with its single:
'"A string with its ' is a single quoted string. The shell strips the 's and is left with the text "A string with its.
single has no metacharacters in it, so the shell leaves this alone.
The output is piped to the quoted command
The output is piped to the regex command
The output is piped to the alternatives" command
The shell sees the single quoted string '=', strips the quotes leaving you with a literal = which it appends to the word alternatives
No syntax error.
When the shell attempts to set up the pipeline it looks for the alternatives" command.
It doesn't find one in the directories it its $PATH, so it stops with the message /bin/sh: 1: /bin/sh: 1: regex: not foundalternatives": not found.
One possible encoding:
.PHONY: this-workes-nicely
this-workes-nicely:
echo $(whatIWant)
though you'll probably find it's cleaner to leave the quotes outside the variable definition in the first place.
We have to make a script that interacts with the standard input, wich is a file and we put a keystring on it, but the difficulty of this exercise is that the file can't be found in some cases, so we have to save the filename to a variable
key.sh (keystring) < (filename)
how can i save the filename into a variable?
In key.sh, you want to have a script like this:
#!/bin/sh
# assign the input string to the variable filename
filename="$1"
Then you would actually call the script with key.sh filename
I am working on a shell script to retrieve variable content from a JSON file via JQ. The JSON file is in string format (no matter whether this is a real string or a number) and to retrieve the variable in my bash script I did something like this
my_domain=$(cat /vagrant/data_bags/config.json | jq ."app"[0]."domain")
The above code once echoed results in "mydomain" with a beginning and a trailing quote sign. I though this was a normal behaviour of the echo command. However, while concatenating my variable with another shell command the system raise an error. For instance, the following command
cp /vagrant/public_html/index.php "/var/www/"+$my_domain+"/index.php"
fails with the following error
cp: cannot create regular file `/var/www/+"mydomain"+/index.php': No such file or directory
At this stage, I wasn't able to identify whether it's me doing the wrong concatenation with the plus sign or the variable is effectively including the quotes that in any case will end up generating an error.
I have tried to replace the quotes in my variable, but I ended up getting the system raising a "Command not found" error.
Can somebody suggest what am I doing wrong?
+ is not used for string concatenation in bash (or perl, or php). Just:
cp /vagrant/public_html/index.php "/var/www/$my_domain/index.php"
Embedding a variable inside a double-quoted text string is known as interpolation, and is one of the reasons why we need the $ prefix, to indicate that this is a variable. Interpolation is specifically not done inside single quoted strings.
Braces ${my_domain} are not required because the / directory separators are not valid characters in a variable name, so there is no ambiguity.
For example:
var='thing'
echo "Give me your ${var}s" # Correct, appends an 's' after 'thing'
echo "Give me your $vars" # incorrect, looks for a variable called vars.
If a variable (like 'vars') does not exist then (by default) it will not complain, it will just give an empty string. Braces (graph brackets) are required more in c-shell (csh or tcsh) because of additional syntax for modifying variables, which involves special trailing characters.
You don't need to use + to concatenate string in bash, change your command to
cp /vagrant/public_html/index.php "/var/www/"${my_domain}"/index.php"
My problem was not related only to the wrong concatenation, but also to the JQ library that after parsing the value from the JSon file was returning text between quotes.
In order to avoid JQ doing this, just add the -rawoutput parameter when calling JQ.