Find all filename.ext1 where no filename.ext2 exists in OS X or the shell

5

2

I need to create both TIF and JPG versions of a large set of images.

All JPG images already exist, but only a part of the TIF images. Is there an easy way to search a directory to find all JPG files that have no corresponding TIF file (i.e. a file with the same name but with different file extension)?

Anders Svensson

Posted 2011-10-10T15:00:56.060

Reputation: 269

Wish I saw this earlier, asked a similar question today. – Rob – 2011-10-27T17:47:04.203

Answers

3

Perl one-liner:

find . -name '*.jpg'|perl -nle 's/\.jpg//;unless(-f "$_.tif"){print "$_.jpg"}' 

In 63 characters:

find . -name '*.jpg'|perl -pnle 's/.jpg//;-f "$_.tif"||"$_.jpg"' 

barrycarter

Posted 2011-10-10T15:00:56.060

Reputation: 695

Interesting... But when I tried it I got the following error (pls note that I don't know perl at all...):

Anderss-MacBook-Pro:Temp Anders$ perl hello.pl String found where operator expected at hello.pl line 2, near "name '.jpg'" (Do you need to predeclare name?) String found where operator expected at hello.pl line 2, near "pnle 's/.jpg//;-f "$.tif"||"$.jpg"'" (Do you need to predeclare pnle?) syntax error at hello.pl line 2, near "name '.jpg'" Execution of hello.pl aborted due to compilation errors. – Anders Svensson – 2011-10-10T21:28:53.307

I'm guessing you just left out something that you assumed I would know :-). BTW, is there any way of making this recursive to check subdirectories as well, with directory names printed out? – Anders Svensson – 2011-10-10T21:30:23.027

Woops, you run this from the command line, not from inside a perl script. Just cut and paste into terminal when you're in the directory w/ the images. This should cover subdirectories, but the files list as "directory/file1", "directory/file2", etc. – barrycarter – 2011-10-10T22:25:34.227

Ok, that's pretty nice! Actually, only the first one worked though, not the second one. That one only listed all the files... Shouldn't you be able to run this in a file though? I read a reeally short intro to perl, where you put the script in a .pl file and ran it from Terminal. That would be good to have in case I need to reuse it, instead of having to paste in this each time... Thanks – Anders Svensson – 2011-10-10T23:03:59.187

Yes, I realized later that the second one was broken. This is really a combination of Unix commands and perl. If you want, you can put it a file called "something.sh" and then do "sh something.sh" (or "bash something.sh"). If you have 'convert' (ImageMagick), you can even convert the jpgs to tiffs. – barrycarter – 2011-10-11T01:12:59.403

7

Assuming all images are in the images directory and has the .jpg suffix, the following little script will print out all image files that has no corresponding .tif file on UNIX:

#!/bin/sh

find images/ -type f -name "*.jpg" |
while read j; do
  t=${j%.jpg}.tif
  if [ ! -f "$t" ]; then
    echo "Lacking tif file: " $j
  fi
done

Paste this to a file, and save it a folder above the one where your images are stored. You could call it find-images. For example:

├── find-images
└── images/
    ├── 1.jpg
    └── 2.jpg
    └── ...

Now, open Utilities/Terminal.app, and use the cd command to navigate to the folder where your script is, e.g. if the script is on your Desktop, just enter cd Desktop.

Then, enter chmod +x find-images. Now you can run the script by just calling ./find-images.

Kusalananda

Posted 2011-10-10T15:00:56.060

Reputation: 1 941

Ok, this sounds sort of doable. I'm on a Mac, which is a kind of Unix, but I'm really new to the Mac. Do I put this script in Terminal or do I somehow create a script file (bash I suppose...)? – Anders Svensson – 2011-10-10T16:43:41.797

@AndersSvensson I added some information about how to run it. KAK, hope you don't mind. – slhck – 2011-10-10T18:00:34.160

This works nicely, and simple enough, thanks. Just a follow-up question: is there any simple way to make it recursive, and check all subdirectories as well? Preferably printing out the name of the directory, like in sections of the result file? – Anders Svensson – 2011-10-10T20:34:23.713

Sorry, just noticed it did actually check recursively... Just ran it on a test folder first which didn't have subdirs... I still have to choose the perl solution, because that worked with all files, this one failed on files with spaces in the names. Thanks though. – Anders Svensson – 2011-10-10T23:03:48.873

@slhck, no worries. – Kusalananda – 2011-10-11T08:23:40.957

@AndersSvensson, strange, I tried it under both Korn shell and bash and it worked with files that had spaces in them. Maybe the OSX bash does something differently from the OpenBSD bash? I haven't set the $IFS variable to anything special, so I don't know what went wrong there. – Kusalananda – 2011-10-11T08:28:10.183

It didn't work with filenames that have spaces because some variables weren't quoted... But I edited the script to quote them, and also changed it to print the full paths of files. – Lri – 2011-10-11T08:37:33.133

6

I'll use Python, since it is cross-platform. First put your jpg and tif files in separate folders.

import os

jpgPath = "path/to/jpgs/folder"
tifPath = "path/to/tifs/folder"

jpgList = [item.rsplit(".", 1)[0] for item in os.listdir(jpgPath)]
tifList = [item.rsplit(".", 1)[0] for item in os.listdir(tifPath)]

diffList = [item for item in jpgList if item not in tifList]

print diffList

Then, save this script to a file, somewhere on your hard drive, maybe under the name find-images.

Now, open Utilities/Terminal.app, and enter python /path/to/file. For example, if you saved it on your Desktop, it would be python ~/Desktop/find-images (since ~ is a shortcut to your home folder). Here, you can run a more detailed explanation on running Python files on your Mac.

Samuel

Posted 2011-10-10T15:00:56.060

Reputation: 323

3You need to strip file extensions first, otherwise none of the files will match. I would guess [item.rsplit(".", 1)[0] for item in os.listdir(jpgPath)]... – user1686 – 2011-10-10T16:04:54.747

Hmm, ok thanks, but I have no idea how to use Python. I'm on a Mac, would Python work in Terminal, or what do I do? @grawity: and where would I place this stripping of the extensions command? – Anders Svensson – 2011-10-10T16:41:58.193

@grawity Oh right. Silly me. Edited. @AndersSvensson Python should be pre-installed on your mac. Just type python into Terminal and paste in the above code. – Samuel – 2011-10-10T17:26:42.290

2

Ah, I felt like adding this. Here's the solution in Ruby, which prints every image that has no TIF counterpart:

jpg = Dir["*.jpg"]
tif = Dir["*.tif"]    
[jpg, tif].each { |a| a.map!{ |f| f[0..-5]} }
puts jpg - tif

Put it into a file which resides in the same folder as your images, save it as find-images, and run it in Terminal by entering ruby find-images.

If you don't know Ruby, don't ask how it works, might take a while to explain :P

slhck

Posted 2011-10-10T15:00:56.060

Reputation: 182 472

1A more generic version: a0, a1 = ARGV[0], ARGV[1]; dir0 = Dir["*.#{a0}"]; dir1 = Dir["*.#{a1}"]; dir0.each {|f| puts f unless dir1.include?(f.sub(/\.#{a0}$/, ".#{a1}"))} – Lri – 2011-10-11T09:34:02.463

@Lri Hah. Pretty neat. Still learning – the parallel assignment is something I have to use more often. – slhck – 2011-10-11T09:38:28.683

2

diffext

#!/bin/bash

found1=$(find "$PWD" -name "*.$1")
found2=$(find "$PWD" -name "*.$2")
export IFS=$'\n'
for f1 in "$found1"; do
    base1=${f1##/}
    for f2 in "$found2"; do
        base2=${f2##/}
        [[ ${base1%.$1} == ${base2%.$2} ]] && continue 2
    done
    echo "$f1"
done
  • Usage: cd somedir; diffext jpg tif
  • Searches recursively in subdirectories of the current directory
    • Treats files as duplicates even if they were in different directories

Lri

Posted 2011-10-10T15:00:56.060

Reputation: 34 501

1

One-liner(s) without perl:

With using while loop after find:

find . -name '*.jpg' | while read file ; do test ! -f `dirname $file`/`basename $file .jpg`.tif && echo $file; done

With using (lots of) subshells:

find . -name '*.jpg' -exec sh -c 'test ! -f `echo {} |sed s/\.jpg$/.flac/` && echo {}' \;

Jens Erat

Posted 2011-10-10T15:00:56.060

Reputation: 14 141

This would work with file names that have spaces and print the absolute paths: diffexts() { find "$PWD" -name "*.$1" | while read f; do [[ ! -e ${f%.$1}.$2 ]] && echo "$f"; done; } – Lri – 2011-10-28T06:06:55.117