This has nothing to do with echo
, echo
just prints the arguments it receives separated by spaces and terminated by a newline (after some potential option processing and backslash escape handling).
What you're seeing here is the effect of shell globbing, aka filename generation aka pathname expansion, whereby when, in list contexts, if some words contain some unquoted globbing pattern operators which include *
, ?
and (except in fish
), [...]
(and possibly more depending on the shell), the word is expanded by the shell to the list of matching files in the directory tree.
That happens regardless of the command, whether it's echo
, rm
, firefox
...
You'll probably have seen it in things like:
rm -- *.txt
There, it's the *
that triggers the behaviour.
In:
echo [0-9a-z]
It's [...]
that triggers it. As a pattern, [0-9a-z]
matches on any one character that is either between 0
and 9
or between a
and z
¹, so as a globbing operator, that [0-9a-z]
word will expand to all the single-character file names in the current working directory that match that pattern.
And rm -- [0-9a-z]
will remove them, vi -- [0-9a-z]
will edit them, etc.
Important note: the command, whether that's rm
, echo
, vi
... only sees the result of the expansion (as separate arguments), not the pattern. In particular, if the first² filename in that expansion starts with -
³, it may be taken as an option with possibly disastrous effects, so, like in cmd -- $files
, it's critical not to forget that --
⁴.
¹ which includes characters such as your 2
, 5
, 8
, h
, q
but also often (and often overlooked) things like ²
, DŽ
,
, ㊻
and thousands more and even sometimes sequences of more than one character which happen to be some collating element sorting in that range in the locale. If you want to only match on the 10 ASCII decimal digits and the 26 lower case ASCII letters of the English alphabet, either use zsh
where the [x-y]
ranges are based on code points or change [0-9a-z]
to [0123456789abcdefghijklmnopqrstuvwxyz]
.
² or following words for those utilities that accept options after non-option arguments such as in many GNU utilities (even though that's not allowed by POSIX).
³ or +
with some utilities such as sh
, sort
, set
.
⁴ the fact that echo
doesn't accept --
as the option delimiter (except for the echo
builtin of fish
, zsh
's accepts -
) is one of the many reasons it can't be used reliably.