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}
, replacex
andy
with any alphabetic or numeric value.In the first form,
{x..y}
will be expanded to all values fromx
toy
, inclusive. For example,{2..5}
will be expanded to2
,3
,4
,5
.In the second explicit form,
{x,y}
will be expanded to onlyx
,y
. For example,{2,5}
will be expanded to2
and5
.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 named2.txt
or5.txt
in the current directory, while for[245].txt
it will match any file named2.txt
or4.txt
or5.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 named2.txt
or3.txt
or4.txt
or5.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.