10

I'd like to ask:

Why is echo {1,2,3} expanded to 1 2 3 which is an expected behavior, while echo [[:digit:]] returns [[:digit:]] while I expected it to print all digits from 0 to 9?

1

3 Answers 3

34

Because they are two different things. The {1,2,3} is an example of brace expansion. The {1,2,3} construct is expanded by the shell, before echo even sees it. You can see what happens if you use set -x:

$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3

As you can see, the command echo {1,2,3} is expanded to:

echo 1 2 3

However, [[:digit:]] is a POSIX character class. When you give it to echo, the shell also processes it first, but this time it is being processed as a shell glob. it works the same way as if you run echo * which will print all files in the current directory. But [[:digit:]] is a shell glob that will match any digit. Now, in bash, if a shell glob doesn't match anything, it will be expanded to itself:

$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files

If the glob does match something, that will be printed:

$ echo /e*c
+ echo /etc
/etc

In both cases, echo just prints whatever the shell tells it to print, but in the second case, since the glob matches something (/etc) it is told to print that something.

So, since you don't have any files or directories whose name consists of exactly one digit (which is what [[:digit:]] would match), the glob is expanded to itself and you get:

$ echo [[:digit:]]
[[:digit:]]

Now, try creating a file called 5 and running the same command:

$ echo [[:digit:]]
5

And if there are more than one matching files:

$ touch 1 5       
$ echo [[:digit:]]
1 5

This is (sort of) documented in man bash in the explanation of the nullglob options which turns this behavior off:

nullglob
    If  set,  bash allows patterns which match no files (see
    Pathname Expansion above) to expand to  a  null  string,
    rather than themselves.

If you set this option:

$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]]  ## prints nothing

$ 
3
  • 4
    See also shopt -s failglob to get a more useful behaviour similar to that of modern shells like zsh or fish. Mar 7, 2018 at 22:24
  • I agree with Stéphane, use failglob. nullglob can cause unexpected issues, e.g. when pasting a URL that happens to have a ?.
    – Kevin
    Mar 7, 2018 at 23:39
  • 1
    Sure, I only mentioned nullglob to demonstrate that the pattern is being interpreted as a glob by the shell.
    – terdon
    Mar 8, 2018 at 0:55
14

{1,2,3} is brace expansion, it expands to the words listed without regard to their meaning.

[...] is a character group, used in filename expansion (or wildcard, or glob) similarly to the asterisk * and question mark ?. It matches any single character listed within, or characters that are members of named groups such as [:digit:] if those are listed. The default behaviour of most shells is to leave the wildcard as-is if there are no files that match it.

(Note that you can't really turn a wildcard/pattern into the set of strings it would match. The asterisk can match any string of any length, so expanding any pattern containing it would produce an infinite list of strings. )

So:

$ bash -c 'echo [[:digit:]]'           # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]'            # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]'           # now there are two matches
1 3                                    # note that d, i, g and t do NOT match

But still:

$ bash -c 'echo {1,2,3}'
1 2 3

Both of those are expanded by the shell, it doesn't matter if the command you're running is ls, or echo or rm. Also note that if either of those is quoted, they will not be expanded:

$ bash -c 'echo "[[:digit:]]"'         # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
5
  • thanks for your answer , am new to linux so let me please ask you how echo is related to files 1 3 , it's function is to print its arguments to stdout not searching for files as to my knowledge Mar 7, 2018 at 19:05
  • 1
    @AbdAllahTalaat this has nothing to do with echo, actually. The shell (e.g. bash) will "expand" the [[:digit:]] before passing it to echo, so echo never sees [[:digit:]], it sees only 1 3. You can see this in action by running set -x which will print the actual commands being run (run set +x to turn it off again).
    – terdon
    Mar 7, 2018 at 19:07
  • @AbdAllahTalaat, echo doesn't look for files, the shell does, before running the echo.
    – ilkkachu
    Mar 7, 2018 at 19:07
  • Especially since I think in DOS/Windows the utilities expand the wildcards, not the shell. (I maybe wrong)
    – ilkkachu
    Mar 7, 2018 at 19:13
  • sorry guys I shifted the correct answer to tedron's answer because his comment contained the meaning that bash is what does the work not echo ... his answer too contained that meaning .. all of you helped me ... i wished if i could put correct answer for all your answers and comments Mar 7, 2018 at 19:17
4

{1,2,3} (and e. g. {1..3} are brace expansions. They are interpreted by the shell before command execution.

[[:digit:]] is a pattern matching token, but you're not using it in a location with any files which match that pattern. If you use a pattern match which has no matches, it expands to itself:

$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3
1
  • No, as the other answers correctly indicate, the pattern is matched against filenames. Mar 8, 2018 at 15:47

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.