5

I often use brace expansion on the command line, because it's handy.

Example

{foo,bar}

expands to:

foo bar

Multiple brace expansion also expand, e.g.:

{foo,bar}Q{foo,bar}

would expand to:

fooQfoo fooQbar barQfoo barQbar

This is expected behavior, where the brace expansions are used in order.

My Question

Now, I'd like the output of any brace expansion (or other short command line trick) to be:

fooQfoo barQbar

Note: I'm using bash 3.2

  • 2
    Do you really want ls to not report existing files, or is this an XY problem? – jasonwryan Sep 2 '13 at 7:21
  • @jasonwryan I thought I was clearly asking for a brace expansion trick, but I updated the question to remove any ambiguity. – Bernhard Sep 2 '13 at 7:27
  • Your edit hasn't made it any clearer to me. ls is a terrible example; tell us what you are actually trying to do... – jasonwryan Sep 2 '13 at 7:33
  • 1
    I want to improve my understanding of shell-usage and brace expansion. It could be used on any command, but e.g. vi -o dir1/file1 dir2/file2 or meld dir3.2/code/vers3.2 dir5.6/code/vers5.6 etc. It is about command-line usage, not about any specific thing. Hope it is clear with my last edit. – Bernhard Sep 2 '13 at 8:00
  • Brace expansion just isn't as expressive as you want it to be; it was designed for a single, interactive purpose. There are other mechanisms that have the power you want. – msw Sep 2 '13 at 18:33
5

I couldn't figure out how to get this done using only curly braces. I don't see a way to achieve this either, so unless someone more clever than I can figure out a way I'd say it's not possible.

As an alternative

Sample Data

$ tree
.
|-- dir1
|   |-- file1
|   `-- file2
`-- dir2
    |-- file1
    `-- file2

Examples

$ seq 2 | xargs -i{} echo dir{}/file{}
dir1/file1
dir2/file2

This can be put into a command like this:

$ echo $(seq 2 | xargs -i{} echo dir{}/file{})
dir1/file1 dir2/file2

or this:

$ ls $(seq 2 | xargs -i{} echo dir{}/file{})
dir1/file1  dir2/file2

or this:

$ ls -l $(seq 2 | xargs -i{} echo dir{}/file{})
-rw-rw-r-- 1 saml saml 0 Sep  2 03:18 dir1/file1
-rw-rw-r-- 1 saml saml 0 Sep  2 03:31 dir2/file2

Why curly braces can't do this

If you look at your original example:

{foo,bar}Q{foo,bar}

The way this gets expanded is as follows:

fooQfoo fooQbar barQfoo barQbar

The mechanism that expanded this is called a Cartesian Product.

For example:

$ echo {A,B}{X,Y,Z}
AX AY AZ BX BY BZ

Or this:

$ echo {M,N}-{A,B}{X,Y,Z}
M-AX M-AY M-AZ M-BX M-BY M-BZ N-AX N-AY N-AZ N-BX N-BY N-BZ

There is no way to create a Cartesian Product that will result in:

fooQfoo barQbar

You're only option is to either resort to trickery such as this:

$ echo dir{1,2}/file{2,1}
dir1/file2 dir1/file1 dir2/file2 dir2/file1

And then put this into a Bash array:

$ a=(dir{1,2}/file{2,1})
$ echo ${a[@]:1:2}
dir1/file1 dir2/file2

The other option would be some "other method" such as the one I previously discussed above (using xargs) for example.

References

  • Thanks for this answer, the xargs solution will work in general, but seems too complicated for the simple solution I am looking for (see my edit if it makes it any clearer). I could take no for an answer if there is some reason behind. – Bernhard Sep 2 '13 at 11:07
  • @Bernhard - I understood you question before. I looked for a while to see if there was a way to do this and I still don't think it's possible. It's a limitation with the regular expressions (the braces) and the way that they expand. There isn't any way to limit them. So if there's a file and/or directory that matches the regex you've provided via the braces, there isn't anything in Bash that will augment the expansion. It's greedy. In Perl and other langs. the notation .* is greedy like this as well. However the langs. provide mechanisms for "controlling" it. – slm Sep 2 '13 at 15:11
  • I was afraid for this answer, hoped for something like {foo,bar}Q{} – Bernhard Sep 2 '13 at 15:36
  • 1
    @Bernhard - here's an example if you want to learn more about braces: echo {w,t,}h{e{n{,ce{,forth}},re{,in,fore,with{,al}}},ither,at} – slm Sep 2 '13 at 16:37
  • Haha, nice one, although it is only nesting :) – Bernhard Sep 2 '13 at 17:46
6

I agree with slm; I've never seen a way to do this solely with brace expansion.

However, using the example in the question, I think the simplest way to get the desired output is to use a loop, rather than brace expansion and additional postprocessing:

$ echo $(for X in foo bar;do echo ${X}Q${X};done)
fooQfoo barQbar
  • I'd agree that this seems like the simplest and most readable option. – kevinmicke Nov 16 '17 at 20:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.