22

How can I convert a string containing glob characters such as

/var/lib/gems/*/bin

into a colon-separated string of filenames (i.e. PATH compatible) matching the pattern?

i.e. echo /var/lib/gems/*/bin will return

/var/lib/gems/1.8/bin /var/lib/gems/1.9.1/bin

I want

/var/lib/gems/1.8/bin:/var/lib/gems/1.9.1/bin 

instead.

The obvious approach is simply to replace the space character with ':' via tr, but that doesn't work if the filename itself contains the space character.

mjs
  • 63,493
  • 27
  • 91
  • 122

8 Answers8

20

Actually, I thought of a better solution: use a shell function.

function join() {
    local IFS=$1
    shift
    echo "$*"
}

mystring=$(join ':' /var/lib/gems/*/bin)
Sean
  • 29,130
  • 4
  • 80
  • 105
13

This should do it for you:

dirs=(/var/lib/gems/*/bin)    # put filenames (dirnames) in an array
saveIFS=$IFS IFS=':'          # set the Internal Field Separator to the desired delimiter
dirs=("${dirs[*]}")           # convert the array to a scalar with the new delimiter
IFS=$saveIFS                  # restore IFS
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
6
PATH="$(printf "%s:" /usr/*/bin)"
PATH="${PATH%:}"
timo
  • 61
  • 1
4

No need to mess with IFS, zsh can join arrays with a simple variable flag:

dirs=(/var/lib/gems/*/bin(N))
dirs=${(j.:.)dirs}

The (N) on the first line suppresses a warning if there are no files; the (j.:.) joins the array with :s. Works with 0, 1, or multiple matches.

Kevin
  • 53,822
  • 15
  • 101
  • 132
  • 1
    @Michaël not that I've found, though ZSH does often still surprise me so I can't say for sure that it can't. – Kevin Sep 17 '20 at 00:18
3

It's pretty trivial if you drop into Perl:

perl -e 'print join ":", @ARGV' /var/lib/gems/*/bin

Or Python:

python -c 'import sys; print ":".join(sys.argv[1:])' /var/lib/gems/*/bin

Or any number of other popular scripting languages.

Sean
  • 29,130
  • 4
  • 80
  • 105
  • It seems as though this might be the best approach overall (the IFS fiddling is a bit messy), although it is a shame to have have to use an external program... – mjs Aug 08 '10 at 07:58
  • definitely the way to go if you want a one-liner – mdup Apr 18 '13 at 08:07
3

without saving IFS and command substitution

dirs=(/var/lib/gems/*/bin) ; IFS=: eval 'dirs="${dirs[*]}"'
pal
  • 618
  • 4
  • 9
2
printf "%s\n" /var/lib/gems/*/bin | tr "\n" ":"
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • Close! But if there's only one match, you get a colon at the end: "/var/lib/gems/1.9.1/bin:". (And if there's no match, a single colon.) – mjs Aug 07 '10 at 13:40
1

Another oneliner: printf "%s\n" /var/lib/gems/*/bin | paste -s -d':'

But @timo's answer is better in my opinion.

domen
  • 1,819
  • 12
  • 19