Are the only options for bash command expansion:

  • unquoted $(..) whose output is always parsed with dumb string splitting, or
  • quoted "$(..)" whose output is always passed along as a unit?

I'm trying to replicate in bash a fish shell function I created for use on Mac OS. My fish function selection https://superuser.com/a/1165855 takes the selection in the frontmost window and outputs the paths for use in command substitution like ls -l (selection). I was hoping to achieve the same thing in bash perhaps as ls -l $(selection).

I thought it was a matter of quoting and so tried passing linefeed-delimited paths to bash's printf "%q ". However I found that no matter what quoting I wrapped the command substitution output in, it was getting divided at whitespace.

Simplified example:

$ touch file\ a file\ b
$ ls $( echo "file\ a file\ b" ) # want expansion equiv to: ls 'file a' 'file b'
ls: a: No such file or directory
ls: b: No such file or directory
ls: file\: No such file or directory
ls: file\: No such file or directory

It wouldn't be the end of the world if I had to use quoted command substitution like ls -l "$(selection)" but doing that the command's output never gets split, nevermind observing my careful quoting. Is the old backtick syntax any different?

Funny, bash, you've got a lot of features. Has nobody though to allow cmda $(cmdb-that-generates-parameters-for-cmda)? Or does a bash user just avoid any spaces or symbols in filenames (like an animal) to make everything easy? Thanks for any answers.


2 Answers 2


There're multiple ways to do what you want. By default bash using whitespaces as default separator, but you can easily override this using IFS (Internal Field Separator) or use different technique such as enumeration. Below are few examples that first come to mind.


Using special internal variable IFS (Internal Field Separator)


touch 'file a' 'file b'

# Set IFS to new line as a separator
ls -la $( echo 'file a'; echo 'file b' )


Using for loop


touch 'file a' 'file b'
for variable in 'file a' 'file b';do
  ls -la "${variable}"


Using for loop from predefined values


arg 0 1
arg 0  2
arg 0   3
arg 0    N

# Using only new line as a separator

for variable in ${MultiArgs};do
  touch "${variable}"
  ls -la "${variable}"


Using ~ (tilda) as separator


MultiArgs='arg 0 1~arg 0 2~arg0 3~ar g 0 N' # Arguments with spaces

for file in ${MultiArgs};do
 touch "${file}"
 ls -la "${file}";

Is the old backtick syntax any different?

No, it is the same but backticks has some limitation that $() doesn't.

Or does a bash user just avoid any spaces or symbols in filenames (like an animal) to make everything easy?

No, it is Ok to use spaces in filenames as far as one would use correct quoting.

As about $(cmdb-that-generates-parameters-for-cmda)


# first command generate text for `sed` command that replacing . to ###
echo $( for i in {1..5}; do echo "${i} space .";done | sed 's/\./###/g' )

# Another example: $() inside of another $()
for i in {1..5}; do touch "${i} x.file";done # Prepare some files with spaces in filenames

echo "$( ls -la $(for i in ls *.file; do echo "$i"; done))"

If you'd like to pass all parameters in one single line to feed your program transcode you can add to the end your ~/.bashrc file following function:

_tr() {
  local debug
  [ $debug -eq 1 ] && {
    echo "Number of Arguments: $#"
    echo "Arguments: $@"

  transcode "$@"

and call this function from command line like that:

eval _tr $(echo "$out")

Where variable out must be like that: out="'file a' 'file b'".
If would type filenames manually then call to _tr function might looks like:

eval _tr $(echo "'file a' 'file b'")

If you would use some external script in place of $() than external script/program must return list of files quoted exactly like this:

"'file a' 'file b'"
  • change from "${i}.file" to "${i} x.file" ... what were you saying about being ok to use spaces? ;^) Aug 31, 2018 at 21:18
  • And I'm looking to do something like this as a convenience from the command line, not from a script, so boilerplate like setting IFS etc is not appropriate. But thanks anyway, especially for direct answers to my specific questions Aug 31, 2018 at 21:26
  • @PierreHouston Bug fixed :)))
    – Alex
    Aug 31, 2018 at 21:26
  • @PierreHouston For command line operations (without making script) use variant#2, just separate commands with ; character
    – Alex
    Aug 31, 2018 at 21:28
  • Nice fast bug fix :thumbsup:. I don't see how #2 helps me at all. I (almost) literally want to type at a bash prompt: cmda $(cmdb-that-generates-parameters-for-cmda), a more literal example: transcode $(selection). It seems not possible, I'm going to just start using fish-shell on my work Macs. Aug 31, 2018 at 21:33

With xargs:

echo '"file a" "file b"' | xargs ls

Mind the quotes. Note xargs is not a Bash builtin (I'm mentioning this in a context of your "Bash, you've got a lot of features").

With eval which is a Bash builtin:

eval ls $( echo '"file a" "file b"' )

Again, mind the quotes. This may backfire easily. Methods based on changing the IFS (this other answer) seem safer. On the other hand with eval you can even do this:

eval ls $( echo '"file "{a,b}' )

Unfortunately none of these is as simple as the other shell's ls (selection) syntax you began with. However you can simplify the syntax of any(?) solution with a custom function, e.g.:

_(){ "$2" | xargs "$1"; }

This allows you to call _ ls selection, if only selection generates input for xargs right. Even better, this:

_(){ "${@:$#}" | xargs "${@:1:$(($#-1))}"; }

makes _ ls -l "file c" selection possible. I guess you could create a similar function that uses the "change the IFS" method from another answer.

Still, this is not as flexible and elegant as fish's ls (selection). I expect you can do ls (selection1) (selection2) in fish; the above _ function doesn't cover this case.

On the other hand eval ls $(selection1) $(selection2) may work if selection1 and selection2 return strings sanitized with proper quoting and/or escaping. If an attacker can make you select a file named ; rm -rf ~/; you'd better sanitize well; or not use eval in the first place.

  • Right, I wasn't speaking of just bash, but command-line usability when using bash shell in a terminal app on a unix-y system, specifically Mac OS in my case. So all signs point to No, bash just cannot do this. Thanks. Aug 31, 2018 at 21:41
  • meant to say.. bash cannot do this without using eval. Sep 10, 2018 at 18:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .