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)?

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' – Stéphane Chazelas Dec 27 '12 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. – Stéphane Chazelas Dec 27 '12 at 10:47

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.