9

Is it possible to expand an or choice in the shell when reading a file for example.

What I mean by this is that, for instance, grep supports syntax like (A|B) to match A or B in a file.

Similarly, if I have these files:

file1.txt
file2.txt
file3.txt
file4.txt
file5.txt

I could do cat file{1..5}.txt in bash, as it expands the range. Is there an equivalent way to do this for just a couple of files?

E.g. cat file(1|5).txt and only print those 2?

16

The standard file name globbing pattern to match a digit is [0-9]. This matches a single digit:

cat file[0-9].txt

To select only two of these:

cat file[25].txt

For larger numbers than 9, brace expansion will be useful (but see note below for the difference between globbing patterns and brace expansions):

cat file{25..60}.txt

Again, brace expansion allows for individual numbers as well:

cat file{12,45,900,xyz}.txt

(note that in the example above, the brace expansion does not involve an arithmetic loop, but just generates names based on the strings provided).

In bash, with the extglob shell option enabled (shopt -s extglob), the following will also work:

cat file@(12|45|490|foo).txt

The @(...) pattern will match any one of the included |-delimited patterns.


The difference between globbing patterns as [...] and @(...) and brace expansions, is that a brace expansion is generated on the command line and may not actually match any existing names in the current directory. A filename globbing pattern will match names, but the shell won't complain if not all possible name exist. If no matching name exists, the pattern will remain be unexpanded, unless also the nullglob shell option is set, in which case the pattern is removed.

Example:

touch file1

ls file[0-9]

Here, only the file listing for file1 will be shown.

With ls file{0..9}, ls would complain about not finding file0, file2 etc.

In the following example, the first command will only touch existing names that matches the given pattern, while the second line will create files that does not already exist:

touch file[0-9]

touch file{0..9}
  • @thecarpy I'm sorry, but that is not a pattern that would match e.g. file45.txt. The bracket expression [...] works just as in regular expression but uses ! in place of ^ for saying "not in". A [...] pattern will always match a single character. – Kusalananda Feb 11 '18 at 13:41
  • You are right! BTW, my {1,2} is not POSIX compliant either ... learned a few new things today! – thecarpy Feb 11 '18 at 13:52
  • 2
    The fact that brace expansions need not match real files can be extremely useful, if you're using it to (for example) generate patterns to pass to grep, generate URLs to pass to curl, and so on... but it can also be confusing to people who are used to working with globs. – Kevin Feb 11 '18 at 17:32
  • @Kevin Correct. A brace expansion does not need to be about filenames. – Kusalananda Feb 11 '18 at 17:37
7

The syntax to use is file{1,2} which will evaluate to file1 and file2.

$ ls
$ touch file{1,2,3,4,5,6,7,8,9}
$ ls
file1  file2  file3  file4  file5  file6  file7  file8  file9

As Inian pointed out below as well ... it would be easier to do touch file{1..9} in this example case ...

$ ls
$ touch file{1..9}
$ ls
file1  file2  file3  file4  file5  file6  file7  file8  file9

You can also use multiple expressions, like:

$ ls
$ touch file{1..9}{a..z}
$ ls
file1a file1b file1c
[...]
file9x file9y file9z

Yes, the above will create 234 (9 times 26) files.

  • 1
    This file{1,2} syntax is also convenient to rename files : mv some_very_long_filename.txt{,.bak} – Eric Duminil Feb 11 '18 at 20:49
6

Yes you can use brace expansion in the bash shell. For just the couple of files just do file{1..2} or just as file{1,2}

Or if you are worried about the files not being there in some cases, just do a simple loop,

for file in file{1..4}.txt; do
    [ -f "$file" ] || continue
    echo "$file" # Do other actions on file here
done

Or if just concatenating is your only operation on the files and if you are unsure which files could not be present at any point of time, just cat them and suppress the errors. Re-directing standard error to /dev/null would suppress the errors if the file is not available.

cat file{1,5}.txt 2>/dev/null

or use the glob expression file[15] which does not complain about the errors if the file was not found.

cat file[15].txt
  • 2
    Nope, no concerns about files not being there, I just wanted to show the content of 2 files numbered numerically, but that aren't consecutive, so the file{1,5} comma syntax was all I was missing! – Joe Healey Feb 11 '18 at 13:10

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.