20

How do I create an environment variable that is the result of a specific command? Specifically, I want an environment variable ($BWD) that is the basename of $PWD

$ cd /home/devel/Autils/lib
$ echo $PWD
/home/devel/Autils/lib
$ # something here to assign BWD
$ echo $BWD
lib
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Josh
  • 321
  • 1
  • 2
  • 4

3 Answers3

25

In general the sequence foo="$(bar)" will run the command bar and assign the output to the variable.

e.g.

% echo $PWD
/home/sweh
% BWD="$(basename "$PWD")"
% echo $BWD
sweh

This creates a shell variable. If you want to make it into an environment variable (which can be seen by sub-shells) you can export it.

e.g.

export BWD="$(basename "$PWD")"

However, in this case don't need to run a command, but use shell variable expansion

BWD=${PWD##*/}
DopeGhoti
  • 76,081
17

In Bourne-like shells, you create environment variables by marking a shell variable with the export attribute (so it's exported to the environment of the commands that the shell will execute) by using the export builtin utility:

export BWD

To assign a value to a shell variable, the syntax is:

BWD=value

You can make that value the output of command by using command substitution. In the Bourne shell, that was with the `the-command` syntax, but in modern Bourne-like shells, the preferred way is with $(the-command) instead:

BWD=$(the-command)

Usually, you need quotes around command substitutions to prevent split+glob. However, split+glob doesn't occur in assignments to scalar variables, so they're not necessary here.

The command to get the base name of a file path is the basename command.

basename "$PWD"

(the quotes there are necessary as split+glob does occur in arguments to commands).

That would return the base name of the file stored in $PWD, unless that value starts with -, in which case, YMMV as many basename implementations will treat it as an option. So generally, when passing variable data to command, we use a -- to tell the command that what's after is not to be taken as an option even if it starts with - (here, $PWD should always start with / except in very pathological cases, so it's not strictly needed).

BWD=$(basename -- "$PWD")
export BWD

In modern shells, you can combine both with:

export BWD="$(basename -- "$PWD")"

(the quotes are needed in some implementations as we're back in the arguments of commands, though some other implementations do parse the arguments of export like assignments as a special case under some conditions).

One problem with that approach is if the value of $PWD ends in newline characters (unlikely in practice) as command substitution strips all trailing newline characters. So in:

mkdir $'foo\n\n'
cd $'foo\n\n'
export BWD="$(basename -- "$PWD")"

$BWD will contain foo instead of $'foo\n\n'.

Instead, you may want to use shell builtin operators to get that base name. In zsh, that can be done with $PWD:t (t for tail); in all POSIX-like shells (including zsh), that can be done with ${PWD##*/} which removes everything up to the right-most / from the content of $PWD.

It will give a different result however if $PWD is /. In that case, basename returns / but ${PWD##*/} or $PWD:t expand to the empty string instead. For a directory like /foo/bar/ ($PWD usually doesn't end in / though except for / itself), basename and $PWD:t give bar, while ${PWD##*/} give the empty string again.

For a variable that dynamically expands to the basename of $PWD, you may use ksh93 and its discipline function:

ksh93 also has a builtin version of basename provided you have /opt/ast/bin ahead of $PATH. So in ksh93:

$ PATH=/opt/ast/bin:$PATH
$ type basename
basename is a shell builtin version of /opt/ast/bin/basename
$ BWD.get() { .sh.value=${ basename -- "$PWD"; }; }
$ cd /var/log
$ echo "$BWD"
log
$ cd /usr/local
$ echo "$BWD"
local
$ export BWD
$ printenv BWD
local

Above, we're using a ksh93-specific form of command substitution: ${ the-command; } which is more efficient in that it doesn't create a subshell environment to run the command.

For the bash shell, see Prevent command substitution from running when declaring a variable for some approaches.

2

You can use the construct foo="$(command)" to return the output of command into the variable foo. So, for your use-case:

$ BWD="$(basename "$PWD")"
DopeGhoti
  • 76,081
  • 3
    Or BWD=${PWD##*/} – jasonwryan Jan 07 '19 at 20:12
  • True, but does not answer the underlying actual question asked (to wit: how to assign the output of a command into a variable). – DopeGhoti Jan 07 '19 at 20:31
  • 1
    eh? What do you think BWD is? The fact that OP assumes it requires a command does not make it necessary, just misguided. – jasonwryan Jan 07 '19 at 20:40
  • 1
    Again, this specific use-case does in fact not require a command, but the question explicitly asked was "how do I get the output of a command into a variable". – DopeGhoti Jan 07 '19 at 20:51
  • 1
    OP also explicitly says: "Specifically, I want an environment variable ($BWD) that is the basename of $PWD" Don't selectively quote just because you assume it supports your argument, that is bad faith. OP wanted an outcome, and like many posters, assumed there was only one route there. – jasonwryan Jan 07 '19 at 20:54
  • Funny thing is, I don't even remember which of you is the correct interpretation of the question! – Josh Aug 22 '19 at 17:03