Category Archives: Code

Shell script to write today’s and next weekday’s date

Published / by Kevin / Leave a Comment

As is this script has limited purpose, and includes RTF markup not useful to anyone else other than as example.

But, what is useful is the date function that increments to the next ‘weekday’ day of week. Be aware that this script formats into Americanized short date (e.g., 12-31-69). Which may seem odd since I just wrote (and re-wrote a couple dozen times) a script to obliterate that format in file names. Except that I’m using this script to fill out the dates on an order form where the dates are expected to be the familiar, informal conversational format used in the US. Whereas for a file name in a computer, YYYY-MM-DD is a useful sorting method.

A lot of ‘others’ don’t take the time to realize that. I’ve run into vandalized man pages for date that attempt to make admonitions against the US informal conversational date format into some moral indictment. That format appears from the way one would speak the date in the US: December thirty-first, nineteen sixty-nine. The same as one would use more words to say: The thirty-first of December, nineteen sixty-nine. It’s not hard, Europeans. 😀

Also posting the plist used with launchd to run this script every morning.

#! /bin/sh
# http://strawhousepig.net/

# Used with launchd to run every morning user is logged in.
# Runs "at load" in case log in happens after the scheduled time (8:15).
# Purpose: $outfile is meant to be placed as a linked text object in InDesign document.
# But, InDesign (CS2) won't keep text style when updating the link
# unless you load it up with (double escaped) RTF markup. :|

# ProTip: Use '$todate' and '$nextdate' as placeholder text in your RTF file.
#         ie., format a sample RTF file then open, copy, & paste it as plain text here.

outfile=~/Documents/date-today.rtf

# Today's (formatted) date.
todate=$(date -j "+%m-%d-%y" | sed -E 's/0([0-9])/\1/g')

# Next weekday:
# Use today's day-of-week to count days until Monday if day-of-week is greater than 4 (Thursday). 
dofw=$(date +%w)
nextdate=$(date -j -v+$(( ( $dofw>4 )?8-$dofw:1))d "+%m-%d-%y" | sed -E 's/0([0-9])/\1/g')

rtf="{\\\rtf1\\\ansi\\\ansicpg1252\\\cocoartf1038\\\cocoasubrtf360
{\\\fonttbl\\\f0\\\fswiss\\\fcharset0 Helvetica;}
{\\\colortbl;\\\red255\\\green255\\\blue255;}
\\\margl1440\\\margr1440\\\vieww9000\\\viewh8400\\\viewkind0
\\\pard\\\tx720\\\tx1440\\\tx2160\\\tx2880\\\tx3600\\\tx4320\\\tx5040\\\tx5760\\\tx6480\\\tx7200\\\tx7920\\\tx8640\\\sb160\\\ql\\\qnatural\\\pardirnatural

\\\f0\\\fs32 \\\cf0 $todate\\\

$nextdate}"

printf "$rtf" > "$outfile"

Launchd properties file (reflects the above code being named “date-today-write-to-file.sh” and placed into the user’s ‘Documents’ folder) should be placed in the users Library folder, not the root Library (e.g., ~/Library/LaunchAgents/date-today.write-to-file.plist) and loaded with launchctl ~/Library/LaunchAgents/date-today.write-to-file.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>date-today.write-to-file</string>
    <key>Program</key>
    <string>/Users/EXAMPLEUSER/Documents/date-today-write-to-file.sh</string>
    <key>RunAtLoad</key>
    <true/>
    <key>StartCalendarInterval</key>
    <dict>
      <key>Hour</key>
      <integer>08</integer>
      <key>Minute</key>
      <integer>15</integer>
    </dict>
  </dict>
</plist>

AppleScript to write Finder tag from folder name

Published / by Kevin / Leave a Comment

This is a weird one that came from a request on reddit.

Not really useful (to me) as intended, but I envision it could be re-worked to a FolderAction that would help tag photos. Especially if coupled with a file sorter. But incorporating the tag writing into the sorter would be much more sane.

The commented out do shell script lines were meant as a way to read existing tags and then add the folder name after them. That didn’t work out thanks to xattr -p spitting out hexadecimal instead of a plist array, which is the format used when writing the tags. WTF, Apple…

-- http://strawhousepig.net/
on run
    set _drop to {}
    set end of _drop to (choose folder)
    my do_it(_drop)
end run

on open _drop
    my do_it(_drop)
end open

on do_it(_drop)
    display dialog "WARNING: This script will overwrite ALL tags of files in or targeted from the opened folder with the name of the opened folder." with icon 0
    repeat with d in _drop
        if folder of (info for d) is true then
            set f to {}
            try
                tell application "Finder"
                    set _tag to "<string>" & name of (info for d) & "</string>"
                    set _files to (every item in d)
                    repeat with f in _files
                        if alias of (info for f as alias) is true then
                            set f to original item of f as alias
                        end if
                        -- 'xattr -p' will print the value for a named metadata ID. Naturally that value is printed as hexadecimal. :|
                        --                      set f_plist to (do shell script "xattr -p com.apple.metadata:_kMDItemUserTags " & quoted form of POSIX path of (f as alias))
                        --                      set _tag to (do shell script "echo \"" & f_plist & "\" | egrep -o \"<string>*</string>\"") & _tag
                        set tag_plist to "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"><plist version=\"1.0\"><array>" & _tag & "</array></plist>"
                        do shell script "xattr -w com.apple.metadata:_kMDItemUserTags " & quoted form of tag_plist & " " & quoted form of (POSIX path of f)

                    end repeat
                end tell
            on error _err
                display dialog _err
            end try
        end if
    end repeat
end do_it

Script to re-write dates in file names

Published / by Kevin / 1 Comment on Script to re-write dates in file names

Annnnnd I’m done.

Did I say “done?” Now using a regex that will (should) only pick viable dates, although other strings can match. Notably a time string that meets the [01-12].[01-31].[00-99] format. Also added some error checking… or at least that’s the idea. Unsure if working at this time. I don’t believe it do. Only if `date` can’t reformat the string it’s fed will it be able to notice, but that is printed to stdout already. It’s the “Warning:” lines that are ambiguous.

Removed the “error checking” which didn’t work. I suggest making note of any errors reported to stdout while the script runs.

The main extended regex that matches the mm.dd.(yy)yy pattern is now a variable.

#!/bin/sh
# http://strawhousepig.net/

# The following `egrep` (or `grep -E`) should prove useful when checking the 
# storage file. If your text editor supports grep based 'find all' you 
# may be able to highlight the original text that is to be changed.
# egrep -nv '(1[0-2]|0?[1-9])[.-/,]([0-2]?[1-9]|3[0-1])[.-/,](20)?[0-9]{2}(\.[[:alnum:]]+)?[[:space:]]/'

# Name of file in which to store 'mv' statements.
mvstore="mvstore.sh"

# The number of parentheses here affects the `sed` for $e. Currently "\4".
dateregex="(1[0-2]|0?[1-9])[.-/,]([0-2]?[1-9]|3[0-1])[.-/,](20)?[0-9]{2}"

usage="
    This script looks recursively for the date pattern mm.dd.yy and similar
    in file and directory names beginning at 'pwd'. It then generates an 'mv'
    statement for each item with the pattern re-written by 'sed' & 'date'
    to yyyy-mm-dd.

    Running the '-e' option without first creating and checking the storage
    file is not recommended.

        -w    Write the 'mv' statements to the file '$mvstore'
                in the present working directory.
        -e    Evaluate the 'mv' statements as they are generated.
        -h    Displays this helpful text.
\n"

function mvdatef() {
    # Here, 'tail' reverses the order of 'find' after 'egrep' filters the result.
    # That way files in a directory are renamed before the directory is.
    # 'find' might be able to use the regex, but I couldn't work it out. YMMV.
    # This only looks for the pattern at the end of the line or just before an extension.
    # If something isn't working, the regex here is probably where it started.
    find "`pwd`" | egrep '[/[:space:]]'"$dateregex"'(\.[[:alnum:]]+)?$' | tail -r | while read a
    do

        # Escape certain characters so they don't wreck the 'mv' statement later.
        # double quote, single quote, parens, ampersand, dollar sign, and space.
        # Single quote, parens, and ampersand are escaped for the shell after breaking out of the 'sed' statement.
        b=$(echo "$a" | sed -E 's/(["'\'\(\)\&'[:space:]$])/\\\1/g')
        if [ "$b" == "" ]; then
            echo "Error: Could not escape $a" >> $mvstore
        fi
        # Suck out the last instance of our hated date pattern (00.00.00 or 00.00.0000).
        # Also replace dashes, slashes, and errant commas for dots because we've come too far not to.
        c=$(echo "$a" | egrep -o "$dateregex" | tail -1 | sed -E 's/[-/,]/\./g')
        if [ "$c" != "" ]; then
            # 'date' will not accept a 2 OR 4 digit year.
            if [ $(echo $c | egrep -o "[0-9]{4}$") ]; then
                dform="%m.%d.%Y"; else
                dform="%m.%d.%y"
            fi
            d="$(date -j -f "$dform" "$c" "+%Y-%m-%d")"
            if [ "$d" == "" ]; then
                echo "Error: Could not format date from $c" >> $mvstore
            fi
            # This is the 'sed' that finds the date and replaces it with what we made just above.
            # It looks for the pattern at the end of the line (path) but includes the extension if there.
            # It is possible that a version number of some sort will also match.
            # Also possible to do away with the EOL in the regex and just go for the pattern.
            e="$(echo "$b" | sed -E 's/'"$dateregex"'(\.[[:alnum:]]+)?$/'$d'\4/')"
            if [ "$e" == "" ]; then
                echo "Error: Could not replace $c in $a" >> $mvstore
            fi
            # After all that dicking around, this is the mv statement.
            f="mv -- $b $e"

            if [ $1 ]; then
                eval $f
            else
                echo "$f" >> "$mvstore"
            fi
        else
            echo "Error: $c - Could not pull viable date from $b" >> $mvstore
        fi
    done
}

if [[ $1 = "-h" ]] ; then
    printf "$usage"
    exit
elif [[ $1 = "-e" ]]; then
    echo "Evaluating 'mv' statements as they are generated..."
    mvdatef -e
elif [[ $1 = "-w" ]]; then
    echo "Generating file `pwd`/$mvstore and writing 'mv' statements to it..."
    printf "#/bin/sh\n#  `date`\n" > "$mvstore"
    mvdatef
else
    printf "$usage"
    exit
fi

Original, less-good version: http://strawhousepig.net/shell-script-to-re-format-poor-date-format-in-filenames/

Shell script to re-format poor date format in filenames

Published / by Kevin / 1 Comment on Shell script to re-format poor date format in filenames

New, more-good version here: New script to re-write dates in file names

After testing and some using, this is going to change to just write the mv statements to a file and to accept an argument to run them instead. No logging since that will serve the same purpose. This post does not contain the final version. It’s here for the sake of history.

And boom. Or so I hope. This *should* handle dates as “01.02.03” and the equivalent “01.02.2003”.
To-do: Check for administrator privileges or just write the log to ~/. MacOS (nee OS X (nee Mac OS)) has a default user log directory, but GNU does not. I suppose we could check for “~/.log/” and mkdir if false…

Title explains it pretty well. Uses egrep to find date, sed to edit it, mv to rename, and read for user confirmation prompt. Which, btw, can’t be done if you are already doing a read without feeding the new read from somewhere other than stdin. Hence the < /dev/tty at that point in the script. So many strangers to thank for sharing their knowledge online. Thanks, strangers! That should do it.

Why this exists is I had a co-worker who added dates to tons of files in the format “01.02.03”, which resulted in files named “Example filename 01.02.03.xmpl” Same for phone numbers. Clearly this is wrong and something must be done about it (now that new ones won’t be cropping up since he.is.gone). You may be asking yourself how he got away with not getting an e-mailed file bounced back for multiple extensions? My educated guess is that he did and kept doing it the dumb way despite that.

Corrects the above format to “+%Y-%m-%d” (YYYY-MM-DD).

This could probably be done with fewer lines or overall be more betterer, but this is like my first whole shell script. Maybe my years of AppleScript are showing. *meh*
Continue reading

Parsing e-mail for info

Published / by Kevin / Leave a Comment

[UPDATE] Added a version that works on OS X 10.4 to the bottom of this post. Does some different logging and is tighter, so I’ll have to go back to the original and update again.

This was done earlier, but I have since had to change it to match changes in the incoming data. Some of which is a big mystery. In particular a new line character that is input as the “line separator” Unicode character. It would end up in the clipboard as line feed, so I just started plugging in white space character id’s until one hit the mark. Lucky me.
Continue reading

AppleTalk in the current year

Published / by Kevin / Leave a Comment

If you’re anything like me and you have to print to an old AppleTalk printer, server, or RIP, -my condolences- keeping up-to-date and bridging that old connection becomes more difficult every decade. You are left with two choices; Keep an oldish Mac on hand capable of running Mac OS X 10.5 or earlier (a Power PC (PPC) is best in order to run the only browser using a modern cipher suite, TenFourFox from the brilliant and beautiful people at floodgap.com) or you can run an OS built on GNU+Linux, even as virtual machine, and have a fully modern desktop OS. Although in a VM a headless console will have a smaller footprint.
Continue reading

Convert PDF to JPEG using (old version of) Acrobat

Published / by Kevin / Leave a Comment

This script will use Acrobat’s built in conversion settings (set in Acrobat’s preferences) to convert an open PDF to a new JPEG file and place that file in the folder of said PDF. If a file with the same name (including extension) exists it will prompt for a new name, at which point you may enter a new name or keep it the same and replace the old file.

Not allowing Acrobat to name the new file because, on Macintosh, Acrobat has a ridiculously low character limit when auto-generating a file.

Has not been tested with multi-page PDF’s, but I do not think it will handle them correctly [it does not]. Acrobat will do it, but the extra file shuffling will not. Also unknown if having a window (document) minimized will cause any shenanigans.

Also note this is written for Acrobat 7. Why? Because old hardware needs old software.

[UPDATE] Changed to allow multiple selections.

tell application "Adobe Acrobat 7.0 Professional"
try
  set _docs to name of every document
  if (count of _docs) is greater than 1 then
    set _docs to (choose from list _docs with prompt "Select file(s) to convert:" with multiple selections allowed) as list
  end if
  repeat with i in _docs
    set _doc to (every document whose name is i)
    set _path to file alias of item 1 of _doc
    set _temp to (path to temporary items folder as text) & "acrobat_tmp_jpg"
    save item 1 of _doc to file _temp using conversion "com.adobe.acrobat.jpeg"
    tell application "Finder"
      set _name to (text items 1 thru -5 of i) & ".jpg"
      set _proof to ((container of file _path) as text) & _name
      if (exists _proof) then
        set _name to text returned of (display dialog "File '" & _name & "' already exists. Please enter a new name (or leave the same to replace):" default answer _name as text)
        set _proof to ((container of file _path) as text) & _name
      end if
      do shell script "mv " & quoted form of POSIX path of _temp & " " & quoted form of POSIX path of _proof
      reveal file _proof
    end tell
  end repeat
end try
end tell

Video re-encode script

Published / by Kevin / Leave a Comment

The purpose of this long, arduous venture was to find a way to simply reduce the size of video files recorded with a Canon ELPH 330 HS digicam. This camera, and surely others, records 720p, 30fps video at a bitrate of 24Mbps, which is at least twice as much as should be necessary. Remember this is a consumer level point-&-shoot whose main draws were: 10x optical zoom; pocketable. This high bitrate results in a 6 minute video being over 1GB. Too much.
Continue reading

Copy video create date from original file

Published / by Kevin / Leave a Comment

This is the companion script for: Video re-encode script

It uses exiftool to copy the video date atom(s) and setfile (is or was included with Apple’s Developer Tools) to change the file system dates. It will more than likely throw exiftool “File not found” errors and I’m not quite sure why. It has never failed to actually work despite this in my experience, however be very careful to not use this on your original source videos.

Hopefully the settings and prompts make sense.

(* http://strawhousepig.net/

This uses Apple's command line developer tools command 'setfile' to set the filesytem dates.

https://developer.apple.com/download/more/

I believe an Apple Developer account is required for that, but I am not sure.

Uses 'exiftool' to set track date atoms.

https://www.sno.phy.queensu.ca/~phil/exiftool/

*)

-- Set to 'false' to omit initial dialog prompts. Does not affect final confirmation prompt.
property prompt_me : true

-- UTC timezone. Leave blank ("") to not use the timezone setting.
property tzone : "-8:00"

on run
  do_it(false)
end run

on do_it(redo)
  repeat
    if tzone is not "" then set tzone to "-timezone=\"" & tzone & "\" " -- There is a trailing space here.    
    if prompt_me is true then
      if button returned of (display dialog "Choose a TARGET file to set creation and modification dates of.") is "OK" then
        set newfile to choose file with prompt "Select TARGET file to set creation and modified dates of:"
      end if
    else
      set newfile to choose file with prompt "Select TARGET file to set creation and modified dates of:"
    end if
    
    set filepath to newfile as string
    if prompt_me is true then
      if button returned of (display dialog "Choose a SOURCE file to *get* creation and modification dates from.") is "OK" then
        set oldfile to choose file with prompt "Select SOURCE file to get dates from: " & filepath
      end if
    else
      set oldfile to choose file with prompt "Select SOURCE file to get dates from: " & filepath
    end if
    set newfilename to name of (info for of newfile)
    if newfilename is not (name of (info for of oldfile)) then
      set warn_button to button returned of (display alert "File names do not match. Proceed?" as warning buttons {"Cancel", "Start Over", "Proceed"})
      if warn_button is "Cancel" then
        return
      else
        if warn_button is "Start Over" then
          set redo to true
          exit repeat
        end if
      end if
    end if
    tell application "System Events" to set [c_date, m_date] to [creation date of oldfile, modification date of oldfile]
    set [p_date_c, p_date_m] to [c_date, m_date] -- Saving "pretty" date for dialog prompt.
    set c_date to ((month of c_date as integer) & "/" & day of c_date & "/" & year of c_date & " " & hours of c_date & ":" & minutes of c_date & ":" & seconds of c_date) as string
    log c_date
    set m_date to ((month of m_date as integer) & "/" & day of m_date & "/" & year of m_date & " " & hours of m_date & ":" & minutes of m_date & ":" & seconds of m_date) as string
    log m_date
    if button returned of (display dialog "This will set the creation and modification dates of file " & filepath & " to:" & return & return & p_date_c & return & p_date_m & return & return & "Proceed?") is "OK" then
      try
        -- Have to include full path to exiftool binary because AppleScript uses the Bourne shell which doesn't have /usr/local/bin in its path? *rolls eyes*
        do shell script "/usr/local/bin/exiftool " & tzone & "-*date=\"`/usr/local/bin/exiftool -time:CreateDate " & quoted form of POSIX path of oldfile & "`\" -wm w " & quoted form of POSIX path of newfile
      on error theErr
        -- Will throw a "File not found" error if this takes over a certain amount of time. rm must be running faster than exiftool can finish?
        display dialog "The 'exiftool' command said: " & theErr
      end try
      delay 1
      try
        -- Ready for this, exiftool adds "_original" to the end of the file it's going to modify tags/atoms of. This is still 'newfile' but with a new name.
        -- If we manually add "_original" to 'newfile' rm will throw an error looking for newfile_original_original... Wha?
        do shell script "rm " & quoted form of POSIX path of newfile
      on error theErr
        display dialog "The 'rm' command said: " & theErr
      end try
      delay 1
      try
        -- Ready again? The original path to 'newfile' is valid again for this... Wha??
        do shell script "setfile -d '" & c_date & "' -m '" & m_date & "' " & quoted form of POSIX path of newfile
      on error theErr
        display dialog "The 'setfile' command said: " & theErr
      end try
    end if
    exit repeat
  end repeat
  if redo then do_it(false)
end do_it