6

My directory looks like this:

$ ls
total 0
-rw-r--r-- 1 user user 0 Jun 18 22:44 file0
-rw-r--r-- 1 user user 0 Jun 18 22:44 file1
-rw-r--r-- 1 user user 0 Jun 18 22:44 file2
-rw-r--r-- 1 user user 0 Jun 18 22:44 file3
-rw-r--r-- 1 user user 0 Jun 18 22:44 file4
-rw-r--r-- 1 user user 0 Jun 18 22:44 file5
-rw-r--r-- 1 user user 0 Jun 18 22:44 file6
-rw-r--r-- 1 user user 0 Jun 18 22:44 file7
-rw-r--r-- 1 user user 0 Jun 18 22:44 file8
-rw-r--r-- 1 user user 0 Jun 18 22:44 file9

Using bash globbing, I want to match file2 to file6, excluding file3.

Since I could not find an and operator for matching multiple patterns, I figured I just use DeMorgan's law that a AND b is equivalent to !(!a OR !b) and write

!(!(file[2-6])|file3)

which works as expected:

$ echo !(!(file[2-6])|file3)
file2 file4 file5 file6

Is there an easier, more direct way to achieve this?

rewire
  • 101
  • 6
  • 1
    Does the real life issue on your system also include removing a single name from a set of matching names in a short numerical range, like in your question, or is the actual situation more complex? – Kusalananda Jun 19 '22 at 11:28
  • 1
    @Kusalananda I am looking for a solution that works for the _abstract_ problem of matching everything that matches `pattern1` but not `pattern2`. – rewire Jun 19 '22 at 14:37
  • 1
    That makes your example rather deceptive as it's easy to leave out `4` from `file[23456]`. You may want to update your question with a further explanation of the general issue so that people here don't think you're interested in the specific example you are showing (which is now the case). – Kusalananda Jun 19 '22 at 14:44

2 Answers2

4

You can apply two ranges [24-6], where 2 is the first and 4-6 is the second:

$ ls file[24-6]
file2 file4 file5 file6

Another example:

$ ls file[2356]
file2 file3 file5 file6

Documentation:

Ranges

There is one special convention: two characters separated by '-' denote a range. (Thus, "[A-Fa-f0-9]" is equivalent to "[ABCDEFabcdef0123456789]".) One may include '-' in its literal meaning by making it the first or last character between the brackets. (Thus, "[]-]" matches just the two characters ']' and '-', and "[--0]" matches the three characters '-', '.', '0', since '/' cannot be matched.)

schrodingerscatcuriosity
  • 12,087
  • 3
  • 29
  • 57
4

negate range :

printf '%s\n' file[!013789]

As stated here :

If the first character following the ‘[’ is a ‘!’ or a ‘^’ then any character not enclosed is matched.


setting GLOBIGNORE to the file you want to skip:

GLOBIGNORE="file0:file1:file3:file7:file8:file9"
printf '%s\n' file*

see this :

The GLOBIGNORE shell variable may be used to restrict the set of file names matching a pattern. If GLOBIGNORE is set, each matching file name that also matches one of the patterns in GLOBIGNORE is removed from the list of matches.


Using extended globbing:

shopt -s extglob
printf '%s\n' file!([013789])

see extended globbing

DanieleGrassini
  • 2,769
  • 5
  • 17