I have a shell script that I'm trying to write to a file using multiple variables, but one of them is being ignored.
#!/bin/bash
dir=/folder
name=bob
date=`date +%Y`
command > $dir/$name_$date.ext
The $name is being ignored. How can I fix this?
Have you noticed that the _ was "ignored" as well? That's a big hint.
If you use set -u, you'll see the following:
-bash: name_: unbound variable
The way bash parses it, the underscore is part of the variable name.
There are several ways to fix the problem.
The cleanest is the ${var} construct which separate the variable name from its surroundings.
You can also use quotation in various ways to force the right parsing, e.g.: "$dir/$name""_$date.ext"
And in case your variables might contain spaces (now, or in the future) use quotation for words.
command >"$dir/${name}_$date.ext"
command >"${dir}/${name}_${date}.ext"
Both these are fine, just pick one style and stick to it.
Related
I've been trying to customize my bash command prompt with a shell script for some time now, but haven't had much success with it.
I'm trying to turn the prompt into something like this, with the Pos variables being strings or bash commands I've defined into the code.
PS1="[$PosZero][$PosOne][$PosTwo][$PosThree]$"
One thing I've tried to do save the current command prompt to a variable, and try and see if I can edit the contents of the brackets somehow, like so:
DEFAULT=$PS1
But what I've been struggling with is trying to edit the custom command prompt after having applied one already. If I'm attempting to change just one bracket, I want all the other brackets to keep whatever contents they have at the time. Instead, they erase themselves unless I pass the same information into the variables in the script.
I've been trying to find a way to parse the DEFAULT value (with the contents of PS1 within) to take out the contents of the brackets and apply them to the Pos variables. But I do not know how to do this. Does someone know how?
I think, this is what you want:
$ PS1='[$PosZero][$PosOne][$PosTwo][$PosThree]$ ' #Note: Use single quotes here, or escape $ as \$
[][][][]$ PosZero=abcd
[abcd][][][]$ PosOne=pqrs
[abcd][pqrs][][]$ PosZero=1234
[1234][pqrs][][]$ PosTwo=xyz
[1234][pqrs][xyz][]$ unset PosZero
[][pqrs][xyz][]$ PosOne=
[][][xyz][]$
Is there a difference when using variables in a Jenkins project between this:
node index.js ${arg}
and this:
node index.js $arg
Where arg is a parameter for the project.
Update:
Interesting to note that it's not Jenkins-specific.
I think this question should remain as others may assume it's something to do with Jenkins.
In the context of your Jenkins project, it is not needed, except as a matter of style.
The braces are useful in those cases where the shell may not be able to determine the end of a variable name. For instance, if your variable is named this, then you would need the braces if your command was
echo "${this}isatest"
Also, you need them when you want to take advantage of Bash's Shell Parameter Expansions.
It's actually a standard shell syntax.
It's easier to manipulate variables / concatenate the contents of variables into other variable names. e.g.
${foo}bar
You can also perform additional string manipulation with the {}:
STRING="This is a string"
echo ${STRING// /_}
http://www.tldp.org/LDP/abs/html/string-manipulation.html
I also find variables with {} to read better but that's a personal preference.
Generic answer here: When do we need curly braces in variables using Bash?
I'm working on what should be a very simple BASH script. What I want to do is a pull an image from a webcamera using curl and write it to a file whose name is datestamped.
#! /bin/bash
DATE=$(date +%Y-%m-%d_%H-%M)
DIRECTORY1=home/manager/security_images/Studio_1/
TARGET1=${DIRECTORY1}${DATE}.jpg
curl http://web#192.168.180.211/snapshot.cgi > $TARGET1
When I try to run this I am told that there is no such file or directory. I believe this is due to an error in my escaping but I have tried seemingly every combination of quotation marks around the variables at each stage and still can't get it to work. I just don't understand what is going wrong and could really use some pointers towards what I'm doing wrong.
Many thanks
No, it’s just a typo.
DIRECTORY1=home/manager/security_images/Studio_1/
^^
Should be
DIRECTORY1=/home/manager/security_images/Studio_1/
of course.
As for escaping, even though only safe characters are used now, so quotes are technically superfluous, quoting out all $variables in every line by default is a good habit in shell scripting, there are very few cases when you do not want to use them.
Double quoting the redirection target should be enough:
curl http://web#192.168.180.211/snapshot.cgi > "$TARGET1"
Just make sure the path to it exists. You can run your script with
set -xv
to see how variables are interpolated.
I've been trying to figure out what is the purpose of brackets in the bash environment variables. For example, in the below actual example of code, why are some of the definitions using a {} around the PATH, for example, export ...=.../${PATH}. Note also that some of the definitions are different: some use {$ECLIPSE_DIR} with the $ within the brackets; some use ${PATH} with the $ outside of the brackets, and some omit brackets altogether. This code generally works, although sometimes errors like the one shown at the bottom are shown (they appear to be transient), and I'm not sure why such errors only show up sometimes and not others.
What are the common practices concerning ways to include bash environment variables, when should brackets be used, and what is the difference between putting the $ inside and outside of brackets? Also, why do some lines have an "export" before the variable name, and some do not? What is the difference here?
# ECLIPSE
ECLIPSE_DIR=$HOME/eclipse
PATH=${PATH}:{$ECLIPSE_DIR}
# ANT
ANT_HOME=/usr/bin/ant
PATH=${ANT_HOME}/bin:${PATH}
export ANT_HOME PATH
# GRADLE
export GRADLE_HOME=/usr/local/gradle
export PATH=$GRADLE_HOME/bin:$PATH</code>
-bash: export: `/usr/bin/ant/bin:/usr/local/bin:{/Users/me/eclipse}:/usr/bin/scala-2.9.0.1/bin:/usr/local/mysql/bin:/usr/local/bin:{/Users/me/eclipse}': not a valid identifier
The braces are usually used for clarity, but a practical use is breaking up text from variable names. Say I had the following:
$ word="ello"
$ echo "h$word"
hello
$ echo "y$wordw" # bash tries to find the var wordw, and replaces with a blank
y
$ echo "y${word}w"
yellow
Variable names are automatically separated by most punctuation (notably . or /).
echo "$word/$word.$word"
ello/ello.ello
Looking at that error you presented, {$ECLIPSE_DIR} gets the variable expanded and then surrounded with literal open and close braces. I think the solution should be changing it to ${ECLIPSE_DIR}
In response to the export question, export is used to make a variable accessible to the shell that called this script. Any variable set up in a script does not exist once the script is finished unless it is exported. Hence, if you want your PATH to change after running that script, export PATH will have to be called before the script is over.
Braces are used with bash variables to disambiguate between variables. For example, consider this:
VAR=this
echo $VAR_and_that
echo ${VAR}_and_that
The first echo prints nothing, since bash thinks you are trying to echo out the var this_and_that which of course doesn't exist. The second echo doesn't have this problem and outputs this_and_that, since bash knows to expand out the VAR variable due to the braces.
How can I set variables that work everywhere? My .bashrc has:
Questions='/Users/User/Stackoverflow/Questions'
Address='My address'
My file has:
$Questions
$Addres
The command "$ cat my_file" in Shell prints "$Questions" and "$Address", instead of "/Users/User/Stackoverflow/Questions" and "My address". Other places where I would like have the variables are Email and Browser. But I cannot get them working.
How can I have my variables to work in every program?
cat doesn't interpret variables. It simply prints out the contents of a file, byte-for-byte.
If you want to be able to print out the values of the variables with my_file, I would suggest changing it to read
echo "$Questions"
echo "$Address"
Then you can "source" it (kind of like running it in the current shell environment) with
$ source my_file
or
$ . my_file
You might also have to use export in .bashrc, like so:
export Questions='/Users/User/Stackoverflow/Questions'
export Address='My address'
How can I have my variables to work in every program?
You can't. Bash and cat are two separate programs. You set a bash variable, it doesn't mean that cat will behave like bash to interpret it. $VARNAME is a shell syntax, cat is a different program, that share almost nothing with the shell.
You can export the shell variable as an environment variable, but cat is not programmed to replace any text templates, it goes way beyond its purpose.
Instead, you may use sed to perform text template substitutions:
sed -e "s|#QUESTIONS#|$Questions|g; s|#ADDRESS#|$Address|g" file.txt
This will replace all instances of #QUESTIONS# and #ANSWERS# in the file with the contents of $Questions and $Address shell variables. Note that these shell variables must not contain any pipe ("|") symbols, since the suggestion uses them as delimiters.
You can get the "cat" thing to work using some nasty hackery:
eval "$(printf 'cat << END\n%s\nEND' "$(< foo)")"
Where "foo" is the file that contains your text you want the bash parameters expanded from. This solution basically just converts the text into a here document, which does expand bash parameters.
cat <<END
[your text]
END
Limitations:
You can't have a line with just "END" in the text file or the solution will break. It'll think the line with "END" in the text file ends the here document instead of the END in the printf command, and the ouput will end early.
TBH:
This is something you just shouldn't want to do. If you want to make template files, go find a templating system that's built for this. You shouldn't be raping bash into doing something that it isn't built to do. It's a scripting language, not a templating system. It's built to parse scripts with a well defined syntax, not arbitrary text files.
You can't. In order to do that, you'd need the cooperation of every email client writer, every browser writer, every utility writer.
Type env see if the variables exist.
If so, try calling them with ${VAR_NAME}.
After re-iterating I now see your problem. You just can't have a text file, cat out it's content and expect the environments variables to be parsed. You just can't.
If it was a bash script file, and you were to run it, you could echo out the variables and they would be parsed like you want.
From your comment on Paul Tomblin's answer:
Are you sure? I have heard that you can have all kind of things like RSS-reader in Emacs. Perhaps, there is a better cooperation. I am a VIM-user, so I don't know. If someone knows whether Emacs has great cooperation between programs, please do not hesitate to share your knowledge. – UnixBasics (3 mins ago)
Do you want (or would you be satisfied with) an editor that can expand your environment variables?
That is a different (i.e. simpler, and possible) problem.
On purely theoretical level, I suppose a hacked filesystem could do what you want, but it would certainly break all kinds of binary storage. So we add a filesystem flag for text (expandable) and non-text file types. And we'd need a way for the filesystem to know what set of variable to expand, and ad nauseum...
Just add to your ~/.profile or to ~/.bashrc:
export Questions='/Users/User/Stackoverflow/Questions'
export Address='My address'