One liner to set environment variable if doesn't exist, else append - bash

I am using bash.
There is an environment variable that I want to either append if it is already set like:
PATH=$PATH":/path/to/bin"
Or if it doesn't already exist I want to simply set it:
PATH="/path/to/bin"
Is there a one line statement to do this?
Obviously the PATH environment variable is pretty much always set but it was easiest to write this question with.

A little improvement on Michael Burr's answer. This works with set -u (set -o nounset) as well:
PATH=${PATH:+$PATH:}/path/to/bin

PATH=${PATH}${PATH:+:}/path/to/bin
${PATH} evaluates to nothing if PATH is not set/empty, otherwise it evaluates to the current path
${PATH:+:} evaluates to nothing if PATH is not set, otherwise it evaluates to ":"

The answers from Michael Burr and user spbnick are already excellent and illustrate the principle. I just want to add two more details:
In their versions, the new path is added to the end of PATH. This is what the OP asked, but it is a less common practice. Adding to the end means that the commands will only be picked if no other commands match from earlier paths. More commonly, users will add to the front to path. This is not what the OP asked, but for other users coming here it may be closer to what they expect. Since the syntax is different I'm highlighting it here.
Also, in the previous versions, the PATH is not quoted. While its unlikely on most Un*x-like operating systems to have spaces in PATH, it is still better practice to always quote.
My slightly improved version, for most typical use cases, is
PATH="/path/to/bin${PATH:+:$PATH}"

Related

Bash: ensuring a variable is set without erasing any existing value

Let's say I'm running a bash script under set -u. Obviously, for any given variable, I need to ensure that it's set. Something like:
foo=
However, if I want to keep any pre-existing value that might be set by my caller, this would overwrite it. A simple solution to this problem is to do this instead:
: ${foo:=}
But I have some code that does this (more complicated) way:
foo=${foo+$foo}
Now, I know this second way works. My question is, is there any advantage to it over the first way? I am assuming there is but now can't remember what it was. Can anyone either think of an edge case (no matter how obscure) where these two constructs would behave differently, or provide a compelling explanation that they can't?
I can't think of any case where they would differ. They're just alternative logic for the same thing.
The meaning of the simple solution is: If foo is unset/empty, set it to the empty string.
The meaning of your code is: If foo is set, set it to itself, otherwise set it to an empty string.
Your code seems like more work -- why set something to itself? Just do nothing and it will keep its value. That's what the simpler version does.
You can also simplify the simple solution further by removing the : in the parameter expansion.
: ${foo=}
This makes it only test whether foo is unset. If it's set to the empty string, no default needs to be assigned.
My question is, is there any advantage to it over the first way?
Maybe this is subjective, but one advantage is that it clearly looks like a variable assignment. Anyone who sees the command foo=${foo+$foo} will immediately understand that it sets the variable foo (even if they need to look up the ${parameter+word} notation to figure out what it sets it to); but someone who sees the command : ${foo:=} is likely to completely miss that it has the side-effect of modifying foo. (The use of : is definitely a hint that something might be happening, since : itself does nothing; but it's not as blatant.)
And of course, someone searching the script for foo= will find the former but not the latter.
That said, I would personally write this as either foo="${foo-}" or foo="${foo:-}", which still makes clear that it sets foo, but is a bit simpler than foo=${foo+$foo}. I also think that readers are more likely to be familiar with ${parameter-word} than ${parameter+word}, but I haven't asked around to check.

What does "$(#:H)" do in MakeFile?

I was speaking with one of my fellow interns at lunch today who is working almost exclusively in Makefile this summer, and he mentioned that he has no idea what $(#:H) means or does. My google-fu is failing me, as I cannot find it anywhere on the web, and was hoping you guys could help me out.
Assuming this is GNU make then that's just the expansion of the (oddly named) #:H variable. Which isn't a default variable (and isn't a variable that can be set with the normal assignment syntax) and would be an odd choice for a variable name to begin with as it is very close to real variable expansions.
It is much more likely that this is referring to BSD make where :H is a variable modifier which (excerpt from the man page):
:H
Replaces each word in the variable with everything but the last component.

What is the purpose of adding a "." in a path to a file?

While looking over documentation of projects, tools etc. I have noticed individuals prepending a period to a file path:
I have tried with and without and it still works, but why do people use the convention?
It depends on the specific environment whether a . has a specific meaning. In general . stands for the current directory (and .. being the parent directory). If the environment uses a PATH-like lookup system, there may well be a difference between ./foo and foo.
The PATH is an environment variable in many systems which defines several paths in which files are looked up. E.g. PATH=/bar:/baz:.. This mean, when you try to refer to file foo, this will be looked up as /bar/foo, /baz/foo and ./foo, the first one to match wins.
As such, the difference can be important if the system uses such a relative lookup. I'm not clear on whether gulp does specifically. If it doesn't, it's relatively redundant to use ..

Why should I use File.join()?

I wonder why I should use:
puts "In folder #{File.join ENV[HOME], projects}"
Instead of:
puts "In folder #{ENV[HOME]/projects}"
I am aware of that File.join will put the appropriate separator (/ vs \) depending on the OS.
The script is already so tightly tied to what version of ruby you are using, what gems you have installed and so on. My scripts tend not to be like an ORM, (in this case) independent of OS.
I will never run this on Windows (the other dependencies will make the script not to work anyway).
So seems not to be a strong reason for using it, right?
Any of the following :
File.join("first","second")
File.join("first/","second")
File.join("first","/second")
File.join("first/","/second")
Will return
=> "first/second"
Could it be a good reason for you ?
That's only one example I can think of.
Actually, your goal is not to concatenate 2 strings, your goal is creating a path. This looks like a strong reason to use File.join to me.
Haven't used Ruby, but I expect a Path.join to handle corner cases, like paths ending with or without directory separators. Besides, it expresses intent a bit more clearly than string concatenation, and clarity is IMHO almost always a good idea.
I expect join to handle corner cases gracefully, like when ENV[HOME] is empty for some weird reason.
In addition to the other answers your code will be more portable, the correct separator will be used regardless of unix/windows/etc.
be aware of difference between RUBY and PYTHON
RUBY: File.join("","somthing") → "/something"
PYTHON: os.path.join("","somthing") → "something"
RUBY treat empty string as path → I call this a BUG

What are the most important shell/terminal concepts/commands for novice to learn?

ALthough I've had to dabble in shell scripting and commands, I still consider myself a novice and I'm interested to hear from others what they consider to be crucial bits of knowledge.
Here's an example of something that I think is important:
I think understanding $PATH is crucial. In order to run psql, for instance, the PostgreSQL folder has to be added to the $PATH variable, a step easily over looked by beginners.
Concept of pipes. The fact that you can easily redirect output and divide complex task to several simple ones is crucial.
Do yourself a favor and get this book: Learning the Bash Shell
Read and understand:
The Official Bash FAQs
Greg Wooledge's Bash FAQs and Bash Pitfalls and everything else on that site
If you're writing shell scripts, an important habit to get into is to always put double quotes around variable substitutions. That is, always write "$myvariable" (and similarly "$(mycommand)"), never plain $myvariable or $(mycommand), unless you understand exactly why you need to leave them out. (Again, the question is not “should I use quotes?”, it's “why would I want to omit the quotes?”)
The reason is that the shell does nasty things when you leave a variable substitution unquoted. (Those nasty things are called field splitting and pathname expansion. They're good in some situations, but almost never on the result of a variable or command substitution.)
If you leave out the quotes, your script may appear to work at first glance. This is because nasty things only happen if the value of the variable contains some special characters (whitespace, \, *, ? and [). This sort of latent bug tends to be revealed the day you create a file whose name contains a space and your script ends up deleting your source tree/thesis/baby pictures/...
So for example, if you have a variable $filename that contains the name of a file you want to pass to a command, always write
mycommand "$filename"
and not mycommand $filename.

Resources