Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tricky brace expansion in shell

When using a POSIX shell, the following

touch {quick,man,strong}ly

expands to

touch quickly manly strongly

Which will touch the files quickly, manly, and strongly, but is it possible to dynamically create the expansion? For example, the following illustrates what I want to do, but does not work because of the order of expansion:

TEST=quick,man,strong    #possibly output from a program
echo {$TEST}ly

Is there any way to achieve this? I do not mind constricting myself to Bash if need be. I would also like to avoid loops. The expansion should be given as complete arguments to any arbitrary program (i.e. the program cannot be called once for each file, it can only be called once for all files). I know about xargs but I'm hoping it can all be done from the shell somehow.

like image 704
dreamlax Avatar asked May 11 '09 04:05

dreamlax


People also ask

What is brace expansion?

Brace expansion is a mechanism by which arbitrary strings may be generated. Patterns to be brace-expanded take the form of an optional PREAMBLE, followed by a series of comma-separated strings between a pair of braces, followed by an optional POSTSCRIPT.

What is the difference between brace expansion and globs?

There are a few things to know about brace expansion versus globs. Globs and extended globs, are pathname expansion, and expand the pathname. Brace expansion is just that, brace expansion, and only expands braces.

What does shell expansion mean?

Definition of shell expansion : a drawing showing the shell plating of a ship and giving the size, shape, and weight of the plates and their connections.

What is tilde expansion Linux?

Tilde expansion is the process of converting these abbreviations to the directory names that they stand for. Tilde expansion applies to the ' ~ ' plus all following characters up to whitespace or a slash.


2 Answers

... There is so much wrong with using eval. What you're asking is only possible with eval, BUT what you might want is easily possible without having to resort to bash bug-central.

Use arrays! Whenever you need to keep multiple items in one datatype, you need (or, should use) an array.

TEST=(quick man strong)
touch "${TEST[@]/%/ly}"

That does exactly what you want without the thousand bugs and security issues introduced and concealed in the other suggestions here.

The way it works is:

  • "${foo[@]}": Expands the array named foo by expanding each of its elements, properly quoted. Don't forget the quotes!
  • ${foo/a/b}: This is a type of parameter expansion that replaces the first a in foo's expansion by a b. In this type of expansion you can use % to signify the end of the expanded value, sort of like $ in regular expressions.
  • Put all that together and "${foo[@]/%/ly}" will expand each element of foo, properly quote it as a separate argument, and replace each element's end by ly.
like image 76
lhunath Avatar answered Sep 21 '22 07:09

lhunath


In bash, you can do this:

#!/bin/bash
TEST=quick,man,strong
eval echo $(echo {$TEST}ly)
#eval touch $(echo {$TEST}ly)

That last line is commented out but will touch the specified files.

like image 23
paxdiablo Avatar answered Sep 21 '22 07:09

paxdiablo