What is the difference between ls [13] and ls {1,3}?

Solution 1:

  • {} 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

Solution 2:

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.