11

I created three files in my directory using the command touch inv{1..3}.txt. Now I used two commands and their output is pretty much the same. Those two commands are ls inv[13].txt and ls {1,3}.txt.

So how are these two different? How are square brackets different compared to braces? Also I was googling for "linux [..]", but couldn't find anything, so could I know as to what these things are called in linux terminology?

Thanks

14
  • {} is called brace expansion. It has two forms: {x..y} and {x,y}, replace x and y with any alphabetic or numeric value.

    In the first form, {x..y} will be expanded to all values from x to y, inclusive. For example, {2..5} will be expanded to 2, 3, 4, 5.

    In the second explicit form, {x,y} will be expanded to only x, y. For example, {2,5} will be expanded to 2 and 5.

    If no expansion is possible, the pattern is treated literally e.g. in {a..3}.

    Important thing to note is that the brace expansion will be done (if possible) irrespective of any file match, while in file creation/matching.

  • [] is known as a pathname expansion (or globbing) pattern. Unlike brace expansion you can only use it while matching filenames, it will be treated literally if you use it while creating files.

    It has two forms: [xyz...] and [x-y].

    In the first form, it will match any single character inside the square brackets separately I.e. each character is treated individually. So for [25].txt it will match a file named 2.txt or 5.txt in the current directory, while for [245].txt it will match any file named 2.txt or 4.txt or 5.txt.

    In the second form, a range expansion is done first before matching individually, if possible otherwise treated literally. So for [2-5]. txt, it will match any file named 2.txt or 3.txt or 4.txt or 5.txt. If the range can't be expanded, it will treated literally e.g. in case of [a-3].txt.


An important behavioral difference between these two is when there are more patterns to match while less actually is matched. As the brace expansion is done no matter what, it will show error that certain file(s) does not exist while pathname expansion will match whatever possible without any error for the rest:

$ ls {1..4}.txt
ls: cannot access 4.txt: No such file or directory
1.txt  2.txt  3.txt

$ ls [1-4].txt
1.txt  2.txt  3.txt

Portability Note:

The [] pathname expansion operator is defined by POSIX hence portable among Bourne shell and derivatives.

The brace expansion, {}, is not defined by POSIX hence is not portable among all shells. For example, dash (sh in Ubuntu) does not have this. It was borrowed from csh and will work on certain shells only e.g. bash, zsh, ksh93, fish. Make sure the shell you are using supports this before implementing.


Example:

$ touch {1..3}.txt

$ ls {1..3}.txt
1.txt  2.txt  3.txt

$ ls {1,2,3}.txt
1.txt  2.txt  3.txt

$ ls {1..4}.txt
ls: cannot access 4.txt: No such file or directory
1.txt  2.txt  3.txt

$ ls {a..3}.txt
ls: cannot access {a..3}.txt: No such file or directory

$ ls [123].txt
1.txt  2.txt  3.txt

$ ls [1-3].txt
1.txt  2.txt  3.txt

$ ls [1-4].txt
1.txt  2.txt  3.txt

$ ls [a-3].txt
ls: cannot access [a-3].txt: No such file or directory
| improve this answer | |
  • 1
    One should avoid relying on the behavior of [ ] expansion when there are no matches because that can be different depending on shell configuration. If you want to use [ or ] in a filename, it should be quoted in your commands. It is also worth noticing the difference between {13..79} and [13-79]. The first will produce all numbers in the range from 13 to 79, the second will match the characters 1, 3, 4, 5, 6, 7, and 9. – kasperd Sep 3 '16 at 12:33
  • @kasperd Yes, that's what nullglob (or similar) is for. About the [13-79] matching, i think i mentioned clearly in my answer about the per character behavior of []`. – heemayl Sep 3 '16 at 12:50
  • @heemayl I think it would be also worth mentioning that ` {1..3}` form is bashism. This won't work in dash. I think ksh has this feature, but not it's more modern flavors, like mksh and pkdsh – Sergiy Kolodyazhnyy Sep 3 '16 at 19:54
  • @Serg Added the portability note. – heemayl Sep 3 '16 at 20:15
7

The key difference between the two forms is illustrated here:

$ ls inv[15].txt
inv1.txt
$ ls inv{1,5}.txt
ls: cannot access 'inv5.txt': No such file or directory
inv1.txt

inv[15].txt is a glob and will expand the a list of matching files on your file system. This is pathname expansion.

inv{1,5}.txt expands to inv1.txt and inv5.txt regardless of what files are actually on your file system. This is brace expansion.

| improve this answer | |
  • The other key difference is that you can write ba{con,tman} and there's no equivalent using square brackets. IOW, brace expansion isn't only for single characters. – hobbs Sep 3 '16 at 5:56

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.