5

I have a sequence of files, which are given like:

File.Number.{1..10}.txt

I would like to get a array or sequence of which of those files exist. A tedious way is to loop over all of them and append to an array. I am wondering if there is a one-liner that can tell me which of those files do not exist or pull out the number of the file.

3

6 Answers 6

13

(Your question says "files that do exist" in one place, and "files that don't" in another, so I wasn't sure which one you wanted.)

To get the ones that do exist, you could use an extended glob in Bash:

$ shopt -s nullglob
$ echo File.Number.@([1-9]|10).txt
File.Number.10.txt File.Number.1.txt File.Number.7.txt File.Number.9.txt

The shopt -s nullglob enables bash's nullglob option so that nothing is returned if there are no files matching the glob. Without it, a glob with no matches expands to itself.

Or, more simply, a numeric range in Zsh:

% echo File.Number.<1-10>.txt
File.Number.10.txt File.Number.1.txt File.Number.7.txt File.Number.9.txt

It's not like the loop is that bad either, and lends to finding the files that do not exist much better than a glob:

$ a=(); for f in File.Number.{1..10}.txt; do [ -f "$f" ] || a+=("$f"); done
$ echo "these ${#a[@]} files didn't exist: ${a[@]}"
these 6 files didn't exist: File.Number.2.txt File.Number.3.txt File.Number.4.txt File.Number.5.txt File.Number.6.txt File.Number.8.txt
2
  • Is there a simple way to modify the loop you have to also report the number of the file only? Thanks!
    – user321627
    May 17, 2020 at 17:05
  • @user321627, you could do something like n=0; for f in File.Number.{1..10}.txt; do [ -f "$f" ] || n=$((n+1)); done; echo $n to just the count of files that didn't exist without using the array.
    – ilkkachu
    May 17, 2020 at 18:20
9

Just use ls File.Number.{1..10}.txt >/dev/null

Example below.

$ ls
File.Number.1.txt  File.Number.3.txt
$ ls File.Number.{1..10}.txt >/dev/null
ls: cannot access 'File.Number.2.txt': No such file or directory
ls: cannot access 'File.Number.4.txt': No such file or directory
ls: cannot access 'File.Number.5.txt': No such file or directory
ls: cannot access 'File.Number.6.txt': No such file or directory
ls: cannot access 'File.Number.7.txt': No such file or directory
ls: cannot access 'File.Number.8.txt': No such file or directory
ls: cannot access 'File.Number.9.txt': No such file or directory
ls: cannot access 'File.Number.10.txt': No such file or directory
$
1
  • Following that lead: $ (ls File.Number.{1..10}.txt 1> /dev/null) 2> >(wc -l) to get the number of files as well... But with that you're still short of dealing with existing files.
    – Cbhihe
    May 19, 2020 at 13:56
3

In zsh, to reduce a list to the elements that refer to existing entries in a given directory, you could do:

dir=/some/dir
list=(File.Number.{1..10}.txt other-file-{a,b}.txt)

existing_in_dir=($dir/$^list(N:t))
non_existing=(${list:|existing_in_dir})
3

The list of files that exists is simply a glob expansion:

var=$( printf '%s\n' File.Number.[0-9].txt File.Number.[0-9][0-9].txt )

To list the ones that doesn't exist becomes a little longer, in bash:

comm -13 <(echo "$var" | sort) <(printf '%s\n' File.Number.{1..10}.txt | sort)

That will work assuming filenames don't contain newlines.

The first part just list the filenames in the pwd that have one digit or two digits between File.Number. and .txt

The second part just recreates a full list of files being searched.

The comm -13 command will give the complement of list 1 in list 2 (elements in set1 that are not in set2).

1
cd /tmp

base=foo
number=42

touch $base.$number.{3,6}.txt

for f in $base.$number.{1..10}.txt
do
  [ -f $f ] || echo $f
done

rm $base.$number.{3,6}.txt

More care is needed if you have special characters in $base or $number.

1
for ((i=1;i<=10;i++)); do f=File.Number.$i.txt; if [ ! -e $f ]; then echo $f; fi ; done

Well, its technically a oneliner as requested.

3
  • 1
    Welcome to SE! Your answer does not answer the question (no array, no count, no differentiated output between existing and non-existing files). You could simplify the if block with: [ ! -e "$f" ] && echo "$f"
    – Cbhihe
    May 19, 2020 at 10:31
  • 1
    Thank you. You are correct, although I would argue that what was really requested here was "a one-liner that can tell me which of those files do not exist or pull out the number of the file." and that the references to arrays, counts and differentiated output are overinterpretion of the posters intent - can't be sure though. May 19, 2020 at 14:29
  • No nitpick here, ;-) OP mentions array, existing and non-existing files, counts, etc... granted, using non standard English syntax, but still. Anyway, just because I noticed that you were a "new born" to SE, I wanted to bring to your attention that this is not always, but very often strictly about what OP requests... Thank you and, again, feel welcome here !
    – Cbhihe
    May 19, 2020 at 14:34

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.