6

I'm trying to list missing files in a sequence in the terminal. While many answers existed here, they are not generic enough that I managed to adapt it to my situation. So if you can make a generic enough answer that will work for more people, please do it.

I'm doing ls {369..422}.avi >/dev/null to list missing files but I can't use * to match anything that ends with .avi. How can I do this?

The numbers are not in the end of the file but in the middle. So I should need something like *numbers*.avi

4
  • Do you want to match anything that ends in .avi, or anything that contains at least one number and ends in avi? Commented Sep 13, 2017 at 12:42
  • You probably would be better off using find with -regex Commented Sep 13, 2017 at 12:42
  • Anything that ends with ´.avi´ and has numbers in sequence on it Commented Sep 13, 2017 at 12:43
  • @RamanSailopal find will only find existing things. Freedo want's to check what's missing. Commented Sep 13, 2017 at 18:45

4 Answers 4

8
ls *{369..422}*.avi >/dev/null

This will first generate patterns like

*369*.avi
*370*.avi
*371*.avi
*372*.avi
*373*.avi
*374*.avi

through the brace expansion, and then ls will be executed with these patterns, which will give you an error message for each pattern that can't be expanded to a name in the current directory.

Alternatively, if you have no files that contain * in their name:

for name in *{369..422}*.avi; do
    case "$name" in 
        '*'*) printf '"%s" not matched\n' "$name" ;;
    esac
done

This relies on the fact that the pattern remains unexpanded if it did not match a name in the current directory. This gives you a way of possibly doing something useful for the missing files, without resorting to parsing the error messages of ls.

4
  • 1
    I think the final element is to redirect stdout, so that the missing files are reported to the terminal. Commented Sep 13, 2017 at 12:49
  • Thanks. This works! However I'm more confuse than before since seems no files are missing other than the expected ones! Well, thanks anyway! Commented Sep 13, 2017 at 13:03
  • 1
    @Freedo Well, it's better to not have files expected to be missing than to miss files expected to be existing... Commented Sep 13, 2017 at 13:13
  • Can't you just escape the * using a \ , this should prevent the problems with names containing it Commented Sep 13, 2017 at 19:55
3

If you want *numbers*.avi, you can do:

ls *[0-9]*.avi

The [0-9] specifies a character class consisting of all characters between 0 and 9 in your locale. That should be all numbers. So you want to match 0 or more characters (*), then a number [0-9] and then 0 or more characters again (*).

If you need to have more than one number in sequence, use:

ls *[0-9][0-9]*.avi

Finally, if you have files in numerical order and just want to find the missing ones, you could also write a little loop:

for avi in {369..422}.avi; do [ -e "$avi" ] || echo "$avi missing"; done
2
  • If I do your ls it will list all of files even after adding >/dev/null. Commented Sep 13, 2017 at 13:04
  • @Freedo yes, of course. You asked for *numbers*avi which will list everything. If you want to find only missing files, you need to give a better explanation in your question. You show file names that consist entirely of numbers ({369..422}.avi) but the say "The numbers are not in the end of the file but in the middle. So I should need something like *numbers.avi*" So I gave you what you asked for. Note that the loop in the end prints out the missing ones only. I find the loop more useful since you can trivially modify it to do something about each of the missing files. Commented Sep 13, 2017 at 13:41
3

To list which of the *{369..422}*.avi patterns don't match any file, with zsh, you could do:

for p (\*{369..422}\*.avi) () {(($#)) || echo $p} $~p(N[1])

Or more verbosely:

for p (\*{369..422}\*.avi) () {
  if (($#)); then
    echo "$# file(s) matching $p"
  else
    echo >&2 No file matching $p
  fi
} $~p(N)

For files following a fixed pattern like: foo-123-bar.avi, you can also do:

expected=(foo-{369..422}-bar.avi)
actual=($^expected(N))
missing=(${expected:|actual})
print -l $missing

Some of the zsh-specific features in there:

  • x{1..20}y brace expansion expanding to x1y, x2y.... Copied by a few other shells since.
  • for var (values) cmd: short form of for loop
  • () compound-command args: anonymous function like in many other languages.
  • $~p: treat the content of $p as a glob (other shells do that by default, and even word splitting!)
  • (N[1]): glob qualifier for globs to expand to no argument when they don't match any file. [1] select the first matching file only as we only one to tell whether there has been any match.
  • $^array: brace-like expansion for the elements on an array
  • ${array1:|array2}: array subtraction
  • print -l: print in lines
0

You can try with: ls *{369..422}*.avi

5
  • I think the final element is to redirect stdout, so that the missing files are reported to the terminal. Commented Sep 13, 2017 at 12:55
  • a good catch with one minor update: we'd better redirect just the error messages to /dev/null rather than whole output ;). i.e. ls {369..422}.avi 2>/dev/null Commented Sep 13, 2017 at 14:41
  • my interpretation was that the OP wanted to find the missing files and wasn't interested in the matching files Commented Sep 13, 2017 at 14:59
  • Yeah this list all files and not the missing ones Commented Sep 23, 2017 at 9:24
  • You are absolutely right. I misread the author's question. Commented Sep 27, 2017 at 17:19

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.