I want to change files with sed for a series of numbered filemnames (more than 10).
0 - 9 I can easily access using sed -i 's/old/new/g' myfile[0-9]
but this doesn't seem to work for numbers higher than 10. How can I do this instead? like [0-50]
?

- 13,725
- 25
- 71
- 128
-
2Character classes, by their nature, match single characters only. – Charles Duffy Apr 26 '16 at 00:36
-
...possibly a duplicate of http://stackoverflow.com/questions/3148240/why-doesnt-01-12-range-work-as-expected (not voting to close as dupe myself because, as a gold-badge for bash, any close vote I offered would be a dupehammer, and they're not *quite* identical, so I think it's fair that this be up for vote). – Charles Duffy Apr 26 '16 at 00:44
-
BTW, `sed -i` is not specified by POSIX, and actively nonportable between major platforms (its calling convention for the native version on OS X is different from the GNU version's usage). Consider using a different, POSIX-specified in-place editor such as `ex` instead. – Charles Duffy Apr 26 '16 at 00:46
1 Answers
A character class, like [0-9]
or [a-f]
, matches a single character only, by definition. They don't match "numbers", per se -- even if the digits given are numeric, they're being viewed only as character codepoints, not numeric values. That's the same thing in fnmatch()
-style patterns (used here by the shell) as they are in regular expressions.
If you want 0-50, with globbing behavior (matching only files that exist) that can be done by composing multiple character classes, like so:
shopt -s nullglob # if no files match, return empty result
files=( myfile[0-9] myfile[1-4][0-9] myfile5[0] )
# if list of files is nonzero, run sed:
(( ${#files[@]} )) && sed -i -e 's/old/new/g' "${files[@]}"
To explain how that works:
myfile[1-9]
matches 1-9 (if they exist)myfile[1-4][0-9]
matches 10-49 (if they exist)myfile5[0]
matches 50, if and only if it exists.
Putting the list of files into the array and checking the array's length makes sure you don't run sed
without any filenames at all listed, which could happen otherwise because of nullglob
. (Why use nullglob
at all here? Because you don't want myfile5[0]
being passed as a literal filename if no myfile50
exists, which is the default behavior otherwise).
There are some additional extensions to the POSIX sh standard available as well:
If you don't care if files exist (and want to put contents on the command line even if they don't), you can use brace expansion:
sed -i -e 's/old/new/g' myfile{0..50}
Alternately, if you simply care about matching one-or-more numeric digits at the end of the filename, you can use extglobs:
shopt -s extglob
sed -i -e 's/old/new/g' myfile+([0-9])

- 280,126
- 43
- 390
- 441
-
I like the brace expansion. That's new to me. For the others, if you don't feel like extglob, nullglob, you can always use the standard `ls | grep | xargs` pattern. For the last one, a simple `/bin/ls myfile* | grep "myfile[0-9]" | xargs --no-run-if-empty -n 1 sed -i -e 's/old/new/g'` would do it. Which I admit looks much more complicated, but if you're like me and won't remember the shopts, is a good alternative. – Mort Apr 26 '16 at 00:46
-
1@Mort, `ls | grep` is actively buggy / evil. See http://mywiki.wooledge.org/ParsingLs -- it can be *made* safe with enough GNU extensions like `find -print0`, `grep -z -Z`, and `xargs -0`, but that's stuff you have to remember. :) – Charles Duffy Apr 26 '16 at 00:47
-
Nice answer/explanation.... one thing I've never understood about `sed` is why it doesn't allow certain `+` repetition quantifiers (eg. `[0-9]+`). Any idea what the reason behind that might be? – l'L'l Apr 26 '16 at 00:48
-
@Mort, ...take a look at how `xargs` deals with filenames with literal quotes and spaces, too. – Charles Duffy Apr 26 '16 at 00:48
-
@l'L'l, that's not actually a `sed` restriction there re: the filenames -- it's the shell doing the work of replacing the glob expression with the list of names, before it even invokes the program it's running such as `sed` at all. To explain what you're seeing, shell patterns are fnmatch-style globs, not regexes, which is why the syntax differs. – Charles Duffy Apr 26 '16 at 00:49
-
1@l'L'l, ...see http://wiki.bash-hackers.org/syntax/expansion/globs for a description of the relevant syntax. – Charles Duffy Apr 26 '16 at 00:49
-
@I'L'I `sed` can operate using both basic (default) and extended regular expressions (with the `-r` option). In basic regular expressions, for `+` to be a quantifier, it must be escaped: `\+`. In extended, it does not. (I think that `+` is not strictly posix basic re, but it is commonly used and sed has it.) – Mort Apr 26 '16 at 01:15
-
@Mort, ...heh; I took that question to be about expressions in filenames, but indeed, it's not so clear. – Charles Duffy Apr 26 '16 at 01:26