I have a directory of lots of .PDF and .JPG files.

There should be a .JPG file for each .PDF with the same name.
I'm trying to use a command to find .PDF files that doesn't have a .JPG file.

My current command is:

find -iname '*.jpg' -print0|sed 's/jpg$/pdf$/ig' |xargs -0 ls

This prints : No such file or directory error for non-existing files;
the problem is the errors of ls cannot be handled by grep or sed .
Any of these solves my problem:

  • How can I make ls only list files that doesn't exists?
  • How to handle/filter errors of ls with sed/grep?
  • any other way for file existence check (without creating bash script files)?

5 Answers 5


I think you should be using a loop

IFR=$'\0' # because you use -print0 on your find
for jpg_file in `find -iname '*.jpg' -print0`
  pdf_file=`echo "$jpg_file" | sed 's/jpg$/pdf/i'`
  if [ -e "$pdf_file" ]; then
    echo "$pdf_file exist"
    echo "$pdf_file missing"
  • 1
    What shell is that? zsh is the only shell I know that supports having NUL characters in its variables, but it doesn't have a IFR variable. If you have multiline file names, the the sed command would change jpg to pdf at the end of every line, so you should make it: sed '$s/jpg$/pdf/i' Dec 27, 2012 at 10:44

POSIXly, you could do:

find . -name '*.[jJ][pP][gG]' -exec sh -c '
  for i do
    [ -e "${i%.*}.pdf" ] || printf "%s\n" "$i"
  done' sh {} +

If you want to search for PDF files case-insensitively, you would do:

find . -name '*.[jJ][pP][gG]' -exec sh -c '
  for i do
    set -- "${i%.*}".[pP][dD][fF]
    case $1 in
      (*"]") printf "%s\n" "$i"
  done' sh {} +

With zsh,

setopt extendedglob
print -rl -- **/*.(#i)jpg(e{'[[ ! -e $REPLY:r.pdf ]]'})

Would report the jpg files that don't have a corresponding pdf file.

If you want to consider PDF and PdF files:

print -rl -- **/*.(#i)jpg(e{'f=($REPLY:r.(#i)pdf(N)); ((!$#f))'})

Of course, instead of printing them, you loop over them like:

for f (**/...) convert $f $f:r.pdf

:r gives the root name (that is without the extension) of a file name (csh heritage), (#i) turns on case insensitive globbing (when extendedglob is on), The e{some code} globbing qualifier is to run some code to determine whether a file should be globbed or not.

Note that it would skip hidden files (those whose name starts with a dot). Add the D globbing qualifier to add them back.


Here I have the following sample directory.


This command will print out a 2 for each PDF file that has a corresponding JPG file and a 1 for PDFs that don't.

% find . -type f|sed 's/pdf\|jpg//'|sort|uniq -c|sed 's/$/pdf/'
  2 ./a1.pdf
  2 ./a2.pdf
  1 ./a3.pdf
  1 ./a4.pdf

Using the 1 and 2 in the output you can grep for either and pipe this on to xargs or whatever you want to.

For example:

List of files that have JPG.

% find . -type f|sed 's/pdf\|jpg//'|sort|uniq -c|sed 's/$/pdf/' |grep " 1 "|awk 
'{print $2}'

List of files that don't have JPG.

% find . -type f|sed 's/pdf\|jpg//'|sort|uniq -c|sed 's/$/pdf/' |grep " 2 "|awk '{print $2}'

Passing them along to xargs ls.

% find . -type f|sed 's/pdf\|jpg//'|sort|uniq -c|sed 's/$/pdf/' |grep " 2 "|awk '{print $2}'|xargs ls
./a1.pdf  ./a2.pdf

Passing them along to xargs ls -l.

% find . -type f|sed 's/pdf\|jpg//'|sort|uniq -c|sed 's/$/pdf/' |grep " 2 "|awk '{print $2}'|xargs ls -l
-rw-rw-r-- 1 saml saml 0 Dec 27 00:14 ./a1.pdf
-rw-rw-r-- 1 saml saml 0 Dec 27 00:14 ./a2.pdf

This should help you:

find . -iname '*.pdf' -exec bash -c '[[ -f $(echo $(echo {}|sed "s/pdf/jpg/")) ]] || echo $(echo {}|sed "s/pdf/jpg/") not found' \;

PDF files that do not have corresponding JPG files will be echoed.

  • What do you think would happen if someone managed to put a file called x;rm -rf ~;.jpg in the current directory? Beside being non-portable, embedding {} like that in arguments to the commands passed to find's -exec should be banned of one's habits if only for security reasons. Dec 27, 2012 at 10:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .