What does this bash mean: USER=${1:-`id -un`} - bash

I can not understand this line in one of old scripts in my project :
USER=${1:-`id -un`}

It's a bash parameter expansion pattern.
The given statement indicates:
If the value of $1 (the first positional parameter (argument)) is unset or null then the output of the command id -un will be set as variable USER
If the parameter $1 is set and not null then the expansion of $1 will be set as parameter USER.
Also the variable USER should be set session wide upon login, unless you have a very good reason you should not modify it directly. You can use a different variable name in your script as a solution then.
Check the Parameter Expansion section of man bash to get more idea.

Looking at the manual page for id (man id), the command id -un will return the actual name of the user.
The format :- uses what's on the right only if what's on the left isn't set. More can be learned about this syntax here.
Therefore, the code you provided is essentially saying default to user, but override the $USER variable to the value of $1 if it is provided.

Parse it this way:
The ${} tells you it is a parameter expansion
The :- is use a default value of the Right Hand of the :- if the Left Hand is unset
The backticks around the RH runs that command
Example:
$ sv="set v"
$ echo ${sv:-`whoami`}
set v
$ echo ${not_set:-`whoami`}
dawg
There are other forms, including:
${parameter:?word} display an error if the parameter is unset,
$ echo ${not_set:?'is not set to anything'}
-bash: not_set: is not set to anything
${parameter:+word} substitute value of word only if parameter is set
$ echo ${sv:+'substitute for sv'}
substitute for sv
${parameter:=word} assign parameter the value of word if parameter is unset
$ echo ${not_set:='now it IS set'}
now it IS set
$ echo "$not_set"
now it IS set

That line is setting the value of a variable called USER:
USER=....
The right side of the equal has several levels.
A "parameter expansion", the ${ }.
The parameter being expanded is $1. That's why it is ${1 ... }.
The value of $1 is being tested if it is NUL or unset, the ${1:- }.
If there is a value of $1 (other than nul), that value is substituted.
If $1 is NUL or unset, substitute the value at the rigth of the -.
The value between the - and the } is a "command substitution".
The "command execution" executes a command and replace its output.
The command is id -un. You can understand what it does with man id.
The command id -un prints a name. The current user if user is omitted.
The same command could be executed by $(id -un).
That would change the line to this:
$ USER=${1:-$(id -un)}
Which will set USER to the name given as positional parameter 1 or the name of the current user executing the line.
To try it, you can make a function:
$ tf(){ USER=${1:-$(id -un)}; echo "$USER"; }
If an value is given, that value is printed back:
$ tf bind
bind
If a user-name is not given, the executing user name is printed:
$ tf
bize

Related

Reassign value of $1 in bash

I'm trying to reassign the value of the positional command line argument $1 to another variable to use elsewhere in the script, but it never works. I have put the following code in a new script and found that the value of the new variable is always blank.
dirname = $1
echo "This is \$1:$1"
echo "This is \$dirname:$dirname"
The output of this after running ./test.sh someval is:
This is $1:someval
This is $dirname:
As you can see, 'dirname' is blank, even after being assigned the value of $1. I'm new to bash so likely missing something. Any help is appreciated.
Assignments cannot have whitespace around the =. An assignment is a single word containing a =: the name precedes the (first) =, and the value follows it.
dirname=$1

Bash: Combine creating a file and storing the Path in a variable

Is there a way to create a file and store the path into a variable in Bash?
ENV_FILE=./.projects.env
echo > $ENV_FILE
How can I achieve this in one command?
Edit:
From the suggestions in the comments below, it could be done like so:
ENV_FILE=$(> ./.projects.env && echo './.projects.env')
If ENV_FILE is not already set, you can use this:
echo > "${ENV_FILE:=./.projects.env}"
Note: if ENV_FILE is already set to a non-null value, this will leave it unchanged and use its existing value. From the Parameter Expansion section of the bash man page:
${parameter:=word}
Assign Default Values. If parameter is unset or null, the expansion of word is assigned to parameter. The value of
parameter is then substituted. Positional parameters and special
parameters may not be assigned to in this way.
BTW, using echo this way will not fully erase the file, it'll replace its contents with a newline (essentially, a single blank line). For a truly empty file, just don't use any command:
> "${ENV_FILE:=./.projects.env}"
You can try
FILE_NAME=<a file name>
ENV_FILE=$(touch "$FILE_NAME" && realpath "$FILE_NAME")
replace <a file name> with the the name you want to give.
if you don't want to repeat the filename (and eliminate potential typos), you can do this instead...
ENV_FILE=$(> ./.projects.env && echo !$)

Bash assignment value to variable as command substitution and print value output

I would like to achieve this in Bash: echo $(a=1)and print the value of variable a
I test eval, $$a,{}, $() but none of them work as most of the times either I got literally a=1 or in one case (I don't remember which) it tried to execute the value.
I known that I can do: a=1;echo $a but because I'm little fun one command per line (even if sometimes is getting little complicated) I was wondering if is possible to do this either with echo or with printf
If you know that $a is previously unset, you can do this using the following syntax:
echo ${a:=1}
This, and other types of parameter expansion, are defined in the POSIX shell command language specification.
If you want to assign a numeric value, another option, which doesn't depend on the value previously being unset, would be to use an arithmetic expansion:
echo $(( a = 1 ))
This assigns the value and echoes the number that has been assigned.
It's worth mentioning that what you're trying to do cannot be done in a subshell by design because a child process cannot modify the environment of its parent.

What is the difference between ${var:-word} and ${var-word}?

I found the following command in a bash script:
git blame $NOT_WHITESPACE --line-porcelain "${2-#}" -- "$file"
What does this ${2-#} mean? Trying out, it returns the 2nd argument, and "#" if it doesn't exist. According to the documentation, ${2:-#} should do the same. I tried it, and it indeed does the same. What's the difference? Where is it documented? The man page does not seem to say anything about this notation.
From Bash hackers wiki - parameter expansion:
${PARAMETER:-WORD}
${PARAMETER-WORD}
If the parameter PARAMETER is unset (never was defined) or null
(empty), this one expands to WORD, otherwise it expands to the value
of PARAMETER, as if it just was ${PARAMETER}. If you omit the :
(colon), like shown in the second form, the default value is only used
when the parameter was unset, not when it was empty.
echo "Your home directory is: ${HOME:-/home/$USER}."
echo "${HOME:-/home/$USER} will be used to store your personal data."
If HOME is unset or empty, everytime you want to print something
useful, you need to put that parameter syntax in.
#!/bin/bash
read -p "Enter your gender (just press ENTER to not tell us): " GENDER
echo "Your gender is ${GENDER:-a secret}."
It will print "Your gender is a secret." when you don't enter the
gender. Note that the default value is used on expansion time, it is
not assigned to the parameter.

Strange shell behaviour

Here is a simple bash script:
a="asd"
b="qf"
echo "$a.$b"
echo "$a_$b"
It's output is:
asd.qf
qf
Why the second line is not "asd_qf" but "qf"?
Because you haven't defined a variable named a_. For that second printout to work, use:
echo "${a}_$b"
Your second echo displays the value of variable $a_ which is unset.
Use echo "${a}_$b"
The shell has rules about what can go in a variable name, and $a_ is interpreted as the variable named a_ (there is no variable with that name so its value is empty).
You can always add braces to be explicit. In this case, ${a}_$b will clearly identify what the variable name is and the result will be what you expect.

Resources