Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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)?
share|improve this question
up vote 1 down vote accepted

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"
share|improve this answer
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

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 is to take the rest (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.

share|improve this answer

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 {} +
share|improve this answer

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.

share|improve this answer
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

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
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.