3

Automator services embedded as a context menu are a great way to quickly scale down JPEGs in the finder. Right click the file, choose your scaling script, done in seconds. The problem is it obliterates the files' date stamps, overwriting them with the date of the scaling operation.

Is there a way to use Automator to preserve date stamps during scaling?

If not in Automator is there a better way of doing this?

Sample JPEG scaling service created with Automator

  • If you modify a file its date/time stamp its supposed to change and that is quite normal behavior! – user3439894 Jan 3 '16 at 17:55
  • As others have pointed out the Date Modified will change. I use a (free) command line program to copy the Exif dates from within the jpeg to the date field. I also have an Automator script to do this which consists of the simple bash script jhead -ft "$*". If you want this search for jhead. – Milliways Jan 4 '16 at 2:17
  • Hi Milliways, this solution is even simpler than the script that I had in mind. I searched around and found out that I can check the EXIF info using the Photos software or using Get Info (Cmd+I) under More, although the date is not listed separately there. I found Matthias Wandel's jhead program. – SeanJ Jan 6 '16 at 20:18
  • @SeanJ, I've edited the code to combine SetFile commands to only call it once using both the -m and -d flags at the same time. (Should have done it that way in the first place) So you might want to update the Automator Service you uploaded. Also if you'd leave the white space and indentation I have in the code I'd appreciate it as IMO it makes it easier to read and understand. The white space doesn't slow down the execution. – user3439894 Jan 7 '16 at 2:38
3

Here is one way of achieving the stated goal with an Automator Service. Instead of using Scale Images use Run Shell Script with settings, Shell: /bin/bash and Pass input: As arguments and add the following code to it:

for f in "$@"; do

        # Get the creation date time stamp of the target file, saved as 't'.

    t="$(/usr/bin/GetFileInfo -d "$f")"

        # Get the pixel width of the target file and divide it by 2 for a 50% scaling, saved as 'w'.

    w="$(/usr/bin/sips -g pixelWidth "$f" | /usr/bin/grep pixelWidth | /usr/bin/awk '{print $2/2}')"

        # Scale the target file.

    /usr/bin/sips --resampleWidth $w "$f"

        # Set the modified and creation date time stamps of the target file to the saved value held in 't'.

    /usr/bin/SetFile -m "$t" -d "$t" "$f"

done
        # Notify User operation is finished.
/usr/bin/afplay "/System/Library/Sounds/Purr.aiff"

Note that the last line in the code above can be omitted if you are going to use Show Growl Notification, which I don't have and why I used a system sound. This was done under OS X 10.8.5 and you'll need to verify the path to the executables are the same on your system, although they should be the same. The reason I'm using the fully qualified pathname of each executable is because Automator errors out if I don't. You can get the FQP in a Terminal using which, e.g., which sips returns /usr/bin/sips.

Note that GetFileInfo and SetFile are a part of Xcode Tools and if not already installed you'll need to install, which can be triggered by typing GetFileInfo in a Terminal and following through with the prompts.

You can read the manual pages for each of the commands used to garner a more through understanding of what's happening.

Manual Pages for: GetFileInfo, sips and SetFile

Automator Service

  • Excellent script! I played around with sips a little but I didn't manage the 50%. A nice solution. After one sees it, it always looks so simple. Thanks a lot. – SeanJ Jan 6 '16 at 19:56
  • @ others who want to use this cool script w/o creating it themselves from scratch, download the ZIP file and put it in the Services folder (typically /Users/your_user/Library/Services/). Start it by right clicking an image in Finder and selecting the script from the Services sub-menu. Keep in mind that as user343.. said, you need Xcode tools and you may need to adjust the paths in the script. – SeanJ Jan 6 '16 at 20:08
  • @SeanJ, Are you saying you played with sips before you saw my answer and couldn't get it to work? I ask because the script as written sips is given 1/2 the pixel width of the original file as $w which in my tests always worked. – user3439894 Jan 6 '16 at 20:20
0

The filesystem in Mac OS X retains 3 separate timestamps for files, as follows:

ctime - change time (last change time of the file’s owner, permissions

atime - Access time (when the file or contents were last accessed)

mtime - Modification time (when the actual contents were last changed)

While these are set automatically during the various stages of a files life through normal interaction, it's important to understand that they are also all changeable to any (valid) value you want at any time. You can overwrite the last modified time to a previous date, if you really have to, etc, with a simple terminal command:

StuffeMac:~ stuffe$ touch dan.txt
StuffeMac:~ stuffe$ ls -l dan.txt
-rw-r--r--  1 stuffe  staff  0  3 Jan 22:30 dan.txt
StuffeMac:~ stuffe$ touch -mt 201601022359 dan.txt
StuffeMac:~ stuffe$ ls -l dan.txt
-rw-r--r--  1 stuffe  staff  0  2 Jan 23:59 dan.txt
StuffeMac:~ stuffe$ 

So all you need to do, is read the existing time you wish to store, make your changes, and then reset back to the original. All this can be done within Automator.


I'll leave you to play with getting the below into Automator, as I don't have time, but here's a quick and dirty way (with lots of comments, and checks to ensure you understand it and it's not just a load of terminal commands) to read a timestamp, store it in a file for later, and then reapply the same timestamp using the command line.

You should be able to save these as short scripts modified to accept the filename as provided by Automator), and then call those scripts as individual Automator actions


The tricky thing is in capturing the whole date string in one go, as you can see the format changes, and you lose the time in order to display the year for old files (and as we have just rolled into 2016, almost all your files will simply show 2015 instead of the time stamp if you use ls...

drwxr-xr-x+   5 stuffe  staff   170  3 May  2015 Public
-rw-r--r--    1 stuffe  staff     0  2 Jan 23:59 dan.txt

...So you can use other commands, like getfileinfo instead:

StuffeMac-6:~ stuffe$ getfileinfo -m Public
05/03/2015 13:07:33
StuffeMac-6:~ stuffe$ getfileinfo -m dan.txt
01/02/2016 23:59:00

So without further ado, here's a horrible hack that someone can improve on:

Step 1, read the timestamp and store it in a variable, and also in a file so that we can retrieve it later

Set up the file and give it an older modified time as if it were an old file, and list the modified time to prove:

StuffeMac-6:~ stuffe$ touch originalfile.txt
StuffeMac-6:~ stuffe$ touch -mt 201506051200 originalfile.txt
StuffeMac-6:~ stuffe$ ls -l originalfile.txt
-rw-r--r--  1 stuffe  staff  0  5 Jun  2015 originalfile.txt
StuffeMac-6:~ stuffe$ getfileinfo -m originalfile.txt
06/05/2015 12:00:00

Now we read the timestamp, and pass it through a command called awk a couple of times to strip out the space in the timestamp and replace it with a colon, then using / and : as delimiters, print out the date in the format that is required by the touch command later (this is a bit nasty, but essentially the first pass without a delimiter provided uses a space as default, which I replace with a colon, so that the next pass which specifically delimits on : works properly - I couldn't get it to use a space and : and / in one pass... - but readability was more important that efficiency) . Echo the variable to prove you have created it correctly, then dump it in a file so you will have it later, read the file to prove it:

StuffeMac-6:~ stuffe$ origtimestampvariable=`getfileinfo -m originalfile.txt | awk '{print $1":"$2}'|awk -F[/:] '{print $3$2$1$4$5}'`
StuffeMac-6:~ stuffe$ echo $origtimestampvariable
201505061200
StuffeMac-6:~ stuffe$ echo $origtimestampvariable > origtimestampvariable.txt
StuffeMac-6:~ stuffe$ more origtimestampvariable.txt
201505061200

Step 2 Now make changes to the file

Here I just append some text, but your Automator would do it's stuff here, then print the amended modified times to prove it changed:

StuffeMac-6:~ stuffe$ echo "making changes to the file now to change modified time" >> originalfile.txt
StuffeMac-6:~ stuffe$ ls -l originalfile.txt
-rw-r--r--  1 stuffe  staff  55  3 Jan 23:17 originalfile.txt
StuffeMac-6:~ stuffe$ getfileinfo -m originalfile.txt
01/03/2016 23:17:05

Step 3 Put the old timestamp back

Now reset the modified timestamp, by using the touch file as shown at the top of this answer, but automating the bit where we include the date to include the contents of the file we now know includes the timestamp we need in the correct format:

StuffeMac-6:~ stuffe$ touch -mt `cat origtimestampvariable.txt` originalfile.txt

Some more checks to prove the original timestamp is in place:

StuffeMac-6:~ stuffe$ ls -l originalfile.txt
-rw-r--r--  1 stuffe  staff  55  6 May  2015 originalfile.txt
StuffeMac-6:~ stuffe$ getfileinfo -m originalfile.txt
05/06/2015 12:00:00
  • Hi stuffe, thanks for the detailed explanation. I'm still a novice user of Terminal but I managed with your walkthrough. For regular photo shrinking I will go with Automator since the time difference doing it with bash will add up. – SeanJ Jan 6 '16 at 19:46
0

It's possible to do this without the need to download Xcode

With the sips command in the "Run Shell Script" action to replace the "Scale Images" action.

The script use the stat command to get the creation and the modification dates of the file, and the touch command to change the dates of the file.

The bash script:

for f in "$@"
do
    newSize=$(sips -g pixelWidth -g pixelHeight "$f" | awk 'BEGIN{max=0} /pixel/{if(max<$NF){max=$NF}} END{print max/2}')
    if [ $newSize != "0" ]; then
        tDates=($(stat -f "%SB %Sm" -t "%Y%m%d%H%M.%S" "$f"))
        sips -Z $newSize -s formatOptions 95 "$f" && touch -t ${tDates[0]} "$f" && touch -t ${tDates[1]} "$f"
    fi
done

You can change the quality (the number after -s formatOptions) 0-100, the default is 80.

100 = the best quality and larger file, 0 = poor quality and smaller file.

  • Nice answer, although as written it fails with syntax error near unexpected token (' and tDates =($(stat -f "%SB %Sm" -t "%Y%m%d%H%M.%S" "$f"))' so you need to remove the space in tDates =. Also you don't need to download Xcode. Xcode and Xcode Tools are two different things and quite different in size. Xcode is over 4 GB and Xcode Tools less then 200 MB. It's nice though to have a native solution too. – user3439894 Jan 7 '16 at 3:05

You must log in to answer this question.

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