21

I need to append a directory to PKG_CONFIG_PATH. Normally, I would use the standard

export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:$(pyenv prefix)/lib/pkgconfig

but PKG_CONFIG_PATH has not been previously set on my system. Therefore, the variable begins with a : character, which tells it to look in the current directory first. I do not want that. I settled on the following,

export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}${PKG_CONFIG_PATH:+:}$(pyenv prefix)/lib/pkgconfig

but that just seems so ugly. Is there a better way? What is the appropriate way to conditionally append the colon if and only if the variable has already been set?

HalosGhost
  • 4,732
  • 10
  • 33
  • 41
scottbb
  • 548
  • 6
  • 15
  • 2
    https://stackoverflow.com/questions/9631228/how-to-smart-append-ld-library-path-in-shell-when-nounset – sancho.s ReinstateMonicaCellio Dec 21 '17 at 02:47
  • Indeed it is importnat not to have empty entries in `LD_LIBRARY_PATH`, see e.g.: [_Empty Entry in LD_LIBRARY_PATH May Lead to Security Issues_](https://jdhao.github.io/2021/07/03/ld_library_path_empty_item/) – nh2 Nov 04 '21 at 17:16

2 Answers2

23

You are on the right track with the ${:+} expansion operator, you just need to modify it slightly:

V=${V:+${V}:}new_V

The first braces expand to $V and the colon iff V is set already otherwise to nothing - which is exactly what you need (and probably also one of the reasons for the existence of the operator).

Thus in your case:

export "PKG_CONFIG_PATH=${PKG_CONFIG_PATH:+${PKG_CONFIG_PATH}:}$(pyenv prefix)/lib/pkgconfig"
mikeserv
  • 57,448
  • 9
  • 113
  • 229
peterph
  • 30,520
  • 2
  • 69
  • 75
  • 2
    I was using that form, but I decided against it because it felt (slightly) less readable: `${V}${V:+:}W` vs. `${V:+${V}:}W`. Either way, they both feel really ugly. I was hoping for something... more elegant, I guess? – scottbb Oct 18 '14 at 20:06
  • @scottbb - this is the syntax form - it is how it is done. If you want to set a variable's value based on a condition you have to do a test. You can do an inline test as is written here, or you can explicitly test with `test` - either way you're testing the value and writing the varname twice, but this way you do it in a single execution statement - in that way it is *practical*, but I've never met an *elegant* computer. – mikeserv Oct 18 '14 at 20:18
  • @scottbb Thinking of it it really is the same. But I'm afraid you won't get it any better, since you basically need the variable name in the condition and then in the expansion - I don't think there is a shell construct that would do that with just one occurrence of the name. – peterph Oct 18 '14 at 20:23
  • 1
    @mikeserv - I probably should have been more precise than to say I was looking for a more "elegant" solution. I have never really seen this done for appending substrings to PATH-style variables, and it felt somehow like I was missing a better way. In my experience, when I get that feeling, there usually is a better way. That's what I should have said. But your point is well taken. Thanks for your response. Also: in one of your edits of @peterph's comment, you made the comment that I should have quoted the entire argument to `export`. That's a very good point, I miffed that detail as well. – scottbb Oct 18 '14 at 21:27
  • @scottbb - sorry if that came off as abrasive - ive never understood the concept of *elegance* with regards to computing. a computer is a machine - a three-way and/or gate compounded billions of times. it cant count higher than one - in every way, every thing it does is *brute-forced*. some intuitive concepts may be easier to translate than others, but, if so, it is only because the concept sits atop some other brute-forced abstraction. computing, at its heart, is always anything but elegant. – mikeserv Oct 18 '14 at 21:41
  • @mikeserv actually I think the quotes are only necessary around the newly added value - the extant variables are expanded in a way that special characters are preserved, aren't they? – peterph Oct 18 '14 at 21:52
  • @peterph - kind of. it depends on the context. as a *word* parameter expansions expand singly - and they do not glob or field-split in command position. so `var=${parameter:+expansion here}` is fine. but as an argument - in a list context - they do split/glob so `IFS=.;printf 'this is a bunch of argument fields%s\n' ${0+..........}` prints 10 lines. – mikeserv Oct 18 '14 at 22:00
  • For reference, to _prepend_ to `PKG_CONFIG_PATH`: `export "PKG_CONFIG_PATH=/path/to/add${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}"`. Conveniently prepending may also be a bit easier to remember because `${V:+:${V}}` is more symmetric. – nh2 Sep 22 '20 at 17:03
1

Lately, I set up GNU stow on my machines to store user-wide stuff like libraries under ~/.local and ran into troubles when defining LD_LIBRARY_PATH, CPATH and LIBRARY_PATH, inadvertently putting a colon there and so breaking stuff.

Then I found your question and the answer wasn't exactly elegant ;-), and I wrote a small function to handle this, please find it here: https://gist.github.com/rico-chet/0229e4c080d9f51a02535dd25a656a8a

## Copyright (C) 2018 Alex Thiessen <[email protected]>
## Copyright (C) 2018 https://unix.stackexchange.com/users/116858/kusalananda
## SPDX-License-Identifier: GPL-2.0-or-later
## <https://spdx.org/licenses/GPL-2.0-or-later.html>

function join() {
    if [ ${#} -eq 0 ]
    then
        echo "\`join\` appends elements separated by colons to a \`bash\` variable " >&2
        echo "usage: join <variable> <element> [element ...]" >&2
        return 1
    fi
    variable="${1}"

    shift
    export ${variable}="${!variable:+${!variable}:}$(IFS=:; echo "${*}")"
}

// edited as suggested by @Kusalananda

Superlexx
  • 11
  • 3