Applescript search for empty folders

Discussion in 'Mac Programming' started by xstophe, Jul 2, 2008.

  1. macrumors newbie

    #1
    Who can help me.

    I need a script, applescript or automator task, or even a project in xcode. Doens't matter, just need something working.

    What I want to do is the following:

    - I need to define a map (either local or server)
    - the program has to find empty subfolders on this server.
    - program has to make a log file of the empty folders, and give a coloured label to the empty folder.

    I tried with automator, but I can't find to do a search on empty folders.

    Hope someone can help me.

    Thnx
    C.
     
  2. macrumors member

    #2
    Ouch.

    Sweet Jesus! This tiny applescript was painful to write. Anyway let me know if you have questions.

    Code:
    -- Give this directory as a unix path
    set mySearchDirectory to "~/Desktop"
    
    -- This should be a unix path too
    set myReportFile to "~/Desktop/EmptyDirs.txt"
    
    -- I think 1 is red... google finder label index for a list somewhere
    set myLabelColorNumber to 1
    
    
    tell application "Finder"
        -- Let the shell expand these user vars
        set mySearchDirectory to (do shell script "echo " & mySearchDirectory)
        set myReportFile to (do shell script "echo " & myReportFile)
        
        -- Init some things
        set reportText to "Empty Folders:"
        set newline to ASCII character 10
        
        -- Start the find process to search mySearchDirectory for empty directories
        set emptyDirs to do shell script "find " & quoted form of mySearchDirectory & " -type d -empty -print0"
        
        repeat with thisDir in my findSplit(emptyDirs)
            -- Label each returned directory
            set label index of item thisDir to myLabelColorNumber
            
            -- Add this directory to our report text, I'm using posix path, but you can just use thisDir if you want
            set reportText to reportText & newline & (POSIX path of thisDir as string)
        end repeat
        
        -- To get a posix path the file has to exist    
        do shell script "touch " & quoted form of myReportFile
        set myReport to (get POSIX file myReportFile as string)
        try
            set fh to open for access file myReport with write permission
            set eof fh to 0
            write reportText to fh starting at 0 as string
            close access fh
        on error
            close access fh
            display dialog "Couldn't open output file"
        end try
    end tell
    
    to findSplit(input)
        set retVal to {}
        set origDelimiters to AppleScript's text item delimiters
        set AppleScript's text item delimiters to ASCII character 0
        
        repeat with thisItem in every text item in input
            try
                copy thisItem as POSIX file to the end of retVal
            end try
        end repeat
        
        set AppleScript's text item delimiters to origDelimiters
        
        return retVal
    end findSplit
    
    
     
  3. macrumors newbie

    #3
    thnx man,

    I tried it, like you gave it and it worked, found empty folders around my desktop.

    Only thing it didn't do, I changed the mySearchDirectory to ~/Desktop/test, because in this folder I wanted it to search, but it just returns an empty textfile.

    What am I doing wrong?

    greetz
    C.
     
  4. macrumors newbie

    #4
    ok I tried something different, placed a fresh empty map in de subdirectory and it worked.

    First I tried it with al list of directories, and I manually deleted the files from 1 of the folders.
    For me this is empty.

    Probably there are some hidden system files still hanging around in a folder this way? Like the .xxxxx files, with a dot in front of them.

    Is there a way, that the script could ignore these? If there are still some hidden files in the folder, it should be considered empty.

    Hope I am clear, english is not my native language ;).

    thnx

    You should receive a medal for your work ;).
     
  5. macrumors member

    #5
    Non-trivial

    This gets a little trickier. The problem is that the BSD "find" command which I was leveraging doesn't support that type of logic. We're going to have to do a lot more on the applescript level, which could slow things down a lot. It might be worth it to change programming languages at this point. Let me think about it and I'll post another attempt today or tomorrow.
     
  6. macrumors newbie

    #6
    sure take your time, I'm glad your helping me.

    I also have xcode installed on the system, so if you like some other language like C++, I could code it in that.

    Thnx for all the work.
     
  7. macrumors member

    #7
    Progress

    Sorry, I forgot to post this yesterday.

    Here's a python script that does what you want. Using a text editor (in text mode) save this to a file, called for example "find-empty.py". Then from terminal, enable it to run with this command
    Code:
    chmod +x find-empty.py
    Then you can run it on one or more directories for example
    Code:
    ./find-empty.py ~/Desktop
    This will write a report called "Empty Folders Report.txt" to the root of the directory you are scanning (in this case it will show up on your Desktop), and set the finder label of any empty subfolders to orange. It will ignore any files whose names begin with a dot when deciding if a folder is empty.

    BUT there is probably a better way to run this script. You said you have 10.5 and the developer tools installed. Open /Developer/Applications/Utilities/MacPython 2.5. Drag and drop this script onto "Build Applet.app". This will create "find-empty.app"

    If you want to scan a folder or disk, just drag it's icon onto this new app file and it will create your report and label your folders as above without requiring you to use the terminal. There is no progress bar or anything, but you can see if it is having problems with "Console.app". Hope this helps.

    Code:
    #!/usr/bin/python
    
    import sys
    import os
    import tempfile
    import subprocess
    import time
        
    ########################################
    #### Subroutines #######################
    ########################################
    
    # callback for filter below
    def isNonTrivial(fileName):
        # If the fileName starts with a dot, it doesn't count
        if fileName[0:1] == '.':
            return False
        else:
            return True
    
    # function which sets the finder label for OS X files
    def finderLabelFiles(files, labelNum):
        # Write the list of files to label to a temp file
        (tmpFH, tmpPath) = tempfile.mkstemp('.txt')
        os.write(tmpFH, "\n".join(files))
        os.close(tmpFH)
        
        # Open a pipe to and from the applescript interpreter
        asInterpreter = subprocess.Popen(
            "/usr/bin/osascript",
            shell=True,
            stdin=subprocess.PIPE,
            stdout=sys.stdout)
        
        # The script we'll send it
        script = [
            'tell application "Finder"',
            'set fileList to (POSIX file "%s")' % (tmpPath),
            'set labelColor to %i' % (labelNum),
            'try',
            'set fh to open for access fileList',
            'repeat with thisFile in (read fh using delimiter (ASCII character 10))',
            'set label index of item (thisFile as POSIX file) to labelColor',
            'end repeat',
            'close access fh',
            'on error',
            'try',
            'close access fh',
            'end try',
            'end try',
            'end tell'
        ]
        
        asInterpreter.stdin.write("\n".join(script))
        asInterpreter.stdin.close()
        asInterpreter.wait()
        os.unlink(tmpPath)
    
    ########################################
    #### Main ##############################
    ########################################
    
    # Sanity-Check Arguments
    if (not (len(sys.argv) > 1 and map(os.path.isdir, sys.argv[1:]).count(False) < 1)):
        print "Usage: %s directory_to_search [...]" % (sys.argv[0])
        exit()
    
    # For each directory specified on the command line
    for searchDirectory in sys.argv[1:]:
        # Init
        emptyDirs = []
        reportFile = os.path.join(searchDirectory, "Empty Folders Report.txt")
    
        # Main Loop: os.walk returns this tuple for each subdir
        for thisPath, thisPathDirs, thisPathFiles in os.walk(searchDirectory):
            # Does this directory have subdirectories?
            if len(thisPathDirs) == 0:
                # Does this directory have files?
                if len(thisPathFiles) == 0:
                    emptyDirs.append(thisPath)
                else:
                    # Are the files all trivial?
                    filteredFiles = filter(isNonTrivial, thisPathFiles)
                    if len(filteredFiles) == 0:
                        emptyDirs.append(thisPath)
        
        # Were there actually were any empty directories?
        if len(emptyDirs) > 0:
            # Write our report
            try:
                report = open(reportFile, 'w')
                report.write("# Empty Folders in %s on %s:\n" % ( searchDirectory, time.asctime() ) )
                report.write("\n".join(emptyDirs))
                report.close()
            except:
                print "Couldn't write report for %s (%s)" % (searchDirectory, reportFile)
                raise
        
            # Set the finder labels for the empty dirs
            finderLabelFiles(emptyDirs, 1)
        else:
            print 'No empty directories were found in "%s"' % (searchDirectory)
    
    exit()
    
     
  8. macrumors newbie

    #8
    you are great !

    it worked like a charm on 10.5.

    I did it with MacPython like you said.

    Now I can just drag the folder on it and there we have it !

    You like this kind of questions? If you do I have another challenge you could try out, something similar..


    The case:

    I have folders (on a server), which are the following structure.

    0xxxxxxx/0xxxxxxx.jpg or 0xxxxxxx/0xxxxxxx.swf
    where the folder name is the same as the file in it with a zero in front of it.

    I have to get a log, and label it like the empty folders app (if you could do a drag on the app thing for this would be great), of the files that don't match the folder name.

    example

    02134578/02134578.jpg is a good one
    02134578/02134578.swf is a good one
    02134578/02134579.jpg is a bad one (this has to log and label)
    02134578/2134578.jpg is also a bad one (this has to log and label)
    02134578/02134579.swf is also a bad one (this has to log and label)

    etc...

    Is this something simple to do? Like the python app you made for the empty folders problem?

    If I am bothering you with this, just say it ;), you helped me already a great deal. So this is something of a bonus ;). So if you like to do it, be my guest otherwise thnx for the great empty folders app !!! I appriciate it !
     
  9. macrumors member

    #9
    Uno mas

    "Bonus" :rolleyes:

    Anyway, that's not too difficult to do. Most of the work done on the last script can be ported to this one. Use it the same as above.

    Code:
    #!/usr/bin/python
    
    import sys
    import os
    import tempfile
    import subprocess
    import time
        
    ########################################
    #### Subroutines #######################
    ########################################
    
    
    # function which sets the finder label for OS X files
    def finderLabelFiles(files, labelNum):
        # Write the list of files to label to a temp file
        (tmpFH, tmpPath) = tempfile.mkstemp('.txt')
        os.write(tmpFH, "\n".join(files))
        os.close(tmpFH)
        
        # Open a pipe to and from the applescript interpreter
        asInterpreter = subprocess.Popen(
            "/usr/bin/osascript",
            shell=True,
            stdin=subprocess.PIPE,
            stdout=sys.stdout)
        
        # The script we'll send it
        script = [
            'tell application "Finder"',
            'set fileList to (POSIX file "%s")' % (tmpPath),
            'set labelColor to %i' % (labelNum),
            'try',
            'set fh to open for access fileList',
            'repeat with thisFile in (read fh using delimiter (ASCII character 10))',
            'set label index of item (thisFile as POSIX file) to labelColor',
            'end repeat',
            'close access fh',
            'on error',
            'try',
            'close access fh',
            'end try',
            'end try',
            'end tell'
        ]
        
        asInterpreter.stdin.write("\n".join(script))
        asInterpreter.stdin.close()
        asInterpreter.wait()
        os.unlink(tmpPath)
    
    ########################################
    #### Main ##############################
    ########################################
    
    # Sanity-Check Arguments
    if (not (len(sys.argv) > 1 and map(os.path.isdir, sys.argv[1:]).count(False) < 1)):
        print "Usage: %s directory_to_search [...]" % (sys.argv[0])
        exit()
    
    # For each directory specified on the command line
    for searchDirectory in sys.argv[1:]:
        # Init
        badFiles = []
        reportFile = os.path.join(searchDirectory, "Bad Files Report.txt")
    
        # Main Loop: os.walk returns this tuple for each subdir
        for thisPath, thisPathDirs, thisPathFiles in os.walk(searchDirectory):
            # The parent folder of '/some/path/on/your/computer" is "computer"
            parentFolder = os.path.split(thisPath)[1]
            
            # Does this directory have any files?
            for thisFile in thisPathFiles:
                # Ignore files beginning with a dot
                if thisFile[0:1] == '.': continue
                
                # The base name of "123.jpg" is "123"
                thisFileBaseName = os.path.splitext(thisFile)[0]
                
                # Do the comparison
                if parentFolder != thisFileBaseName:
                    badFiles.append(os.path.join(thisPath, thisFile))
    
        
        # Were there actually were any bad files?
        if len(badFiles) > 0:
            # Write our report
            try:
                report = open(reportFile, 'w')
                report.write("# Bad files in %s on %s:\n" % ( searchDirectory, time.asctime() ) )
                report.write("\n".join(badFiles))
                report.close()
            except:
                print "Couldn't write report for %s (%s)" % (searchDirectory, reportFile)
                raise
        
            # Set the finder labels for the empty dirs
            finderLabelFiles(badFiles, 2)
        else:
            print 'No bad files were found in "%s"' % (searchDirectory)
    
    exit()
    
     
  10. macrumors newbie

    #10
    great thnx works like a charm !

    the bad files are labelled now, can I change something so that the dirs of the bad files are labelled? it's easier on the eye If I got a complete list to view. The dirs will stand out.
     
  11. macrumors member

    #11
    I made a couple of small adjustments so that it labels the folders as well as the files. (See attached) If you don't want the files labeled, remove or comment-out line 83 which reads "badItems.append(os.path.join(thisPath, thisFile))"

    View attachment findBad.py.txt
     
  12. macrumors newbie

    #12
    perfect !!

    thx a lot for your work, I hope sometime I can help you back with something !!

    hope to stay on contact with you, maybe I have some new challenges for you in the future ;)
     
  13. macrumors newbie

    #13
    Can't get app to report

    I tried tedsmith3rd post entitled "Progress", but it doesn't seem to report anything. Is there a programming change since he built that app for 10.5? I know nothing about Python, I'm just trying to get rid of some empty folders on an external drive formatted in HFS+ attached to my Airport Extreme.
     

Share This Page