Tips, Journal, CodeOctober 15, 2009 2:38 pm

Today I learned two tricks for the replace() function in Javascript.

  1. Use $? for the found pattern, and
  2. use (parentheses) within your regular expression if you want to refer to what it matched by $number in the replacement.

I needed the sub-string ‘.A.‘ to be zero-padded into ‘.0A.‘ instead. However, I wasn’t positive enough that the only character that needed zero-padding would be an A, so I wanted to match the general case of any single character between dots.

So that means I could match any single character between dots and give a long-winded message: output = input.replace(/\.(.)\./g, "replace> $& <replace "); or more to the point: output = input.replace(/\.(\w)\./g, ".0$1.");

I can use either ‘.‘ (traditional Unix regexp for a single character) or ‘\w‘ (PCRE for any non-whitespace character) to match A with my expected input.

Tips, Journal, CodeOctober 1, 2009 1:00 pm

I ran across a nice piece of CSS to add numbered subitems in an ordered list.

ol { counter-reset: item }
li { display: block }
li:before { content: counters(item, ".") " "; counter-increment: item }

That’s pretty simple! You can play around with HTML lists here.

There’s also the jQuery route (with example), but it isn’t as simple or lightweight, and it isn’t recursive.

Tips, Unix, Code 8:36 am

I wrote a quick bash script about a month ago to do my daily rsync, but I had a problem that took the quick and easy fun out of the script for several days. What worked on the command line to handle spaces didn’t work in the script! The answer was buried in the rsync FAQ:

The [rsync] command line is interpreted by the remote shell and thus the spaces need to arrive on the remote system escaped so that the shell doesn’t split such filenames into multiple arguments.

That’s the core of the problem, but backslashes didn’t seem to help. I went for the ? solution, to match one character that happens to be a space, instead.

Today I discovered that I probably could have stayed with what worked on the command line (but not in the script) if I had used eval on the scripted rsync command.

Tips, Journal, CodeSeptember 18, 2009 8:49 pm

It’s a little thing that comes from trying to wing it and not having a reference book, but I learned that Javascript’s setTimeout is a delay before the command the relevant command, not after. For some reason I wanted it to be the sort of timeout that means to bail out of starting that process if you don’t complete it within the time interval. It’s not. It means run that command after a delay of this many milliseconds which is a different creature entirely.

setTimeout(thatCommand, this)

Tips, Unix, CodeSeptember 15, 2009 1:46 pm

So far, the best way to debug Expect programs seems to be to append “ -d” the #! line. The output is confusingly verbose, but if I work through it one glob at a time, I can usually figure out what’s going on.

Right now, it’s that it’s matching too early. So I think sleep 5 may be the needed breather.

Project, Unix, CodeSeptember 4, 2009 12:59 pm

Some days have little mysteries.

I was happy to learn the bash trick ${0##*/} to skip using basename (don’t need to add dependencies), and once I learned more about bash substring removal, it made perfect sense. $0 (or ${0}) is the script name, as called, often with leading directory information. You need curly braces for substring removal, so start with ${0}. Use # for stingy prefix matching (the smallest match from the start of the variable), and ## for greedy prefix matching (the largest match). So ##*/ is the largest match of any character than ends with a slash. Since / is the directory separator, that greedy match removes all leading directories from the script name … same as basename, but should be faster.

OK, so I feel like I’ve learned something! A small trick, but it weans me from excessive sed and awk too.

Yesterday’s neat trick was shoving all of my command-line arguments into an array. Why an array? I kept losing the quotes around a string with spaces (user comment) with unexpected script results. This is why I test my scripts! So I don’t have to worry about shift eating the script input, I’m in the habit of storing that input in a variable for safe-keeping; I’m now tinkering with an array for that purpose. (Yes, the implicit shift of getopts can be overridden with OPTIND=1, but $ARGS is immune to other tactics like set too.)

# save the args
ARGS="$@"
# or put the args into an array, space-preserving
typeset -a ARGARRAY=("$@")

The ARGARRAY is great: although I lose the quotes from the command line, the user comment is a single element in the array so it’s safe as long as I quote that variable when I use it. But back on the bash string replacement track: using the command-line input argument array, I quickly noticed that the flags starting with a dash (hyphen) disturb some string matching routines. So since I know about greedy string matching now, I thought this should work:

typeset -a UNDASHEDARGARRAY("${@##-}")

It doesn’t work. It still doesn’t work when I escape the hyphen UDAA=("${@##\-}"); either way, the result is just the same as the stingy removal of UDAA=("${@#\-}"): just the first dash goes away. Phooey.

The only approach I’ve found that works is to use the substring removal twice in a row.

typeset -a UDAA=("${@##\-}")
UDAA=("${UDAA[@]##\-}")
(also works with single octothorpes instead of pairs)

What also works is the overkill of removing all hyphens, leading or trailing or internal, with either

typeset -a ONE=("${@//-}") TWO=("${@//-/}")

However, internal hyphens don’t mess up string matching, and might be significant. One or two leading dashes indicate a flag, an option to the command; any other dashes might be useful.

So my little bash mystery today is why these two arrays are the same with --long-flags:
typeset -a FIRST=("${@#-}") SECOND=("${@##-}")

I don’t like these mysteries, but I know when it’s time to get back to work. I have a work-around, so I’ll use it.

UPDATE 14:09: looked at the strip_leading_zero2 () example function in the Advanced Bash-Scripting Guide to strip possible leading zero(s), and came up with a dash-prefix-stripper that works in one operation:

shopt -s extglob
typeset -a UNDASHEDARGARRAY=("${@##+(-)}")

I’ll ponder that one, and check extglob before set then unset it after.

CodeAugust 22, 2009 9:10 pm

Hmm, remote input-output with Expect sounds exactly like where I’ll be next week with expect scripts. However, the whole expect buffer concept doesn’t sit well with me. I can tell it’s powerful, once you grok it, but I’m not there and I doubt I need to get there: I’m good with sed and awk. I understand that the expect buffer holds both sides of the “conversation” without regard for local and remote, and that $expect_out(buffer) contains everything since the last match, but even so, I’m not able to get expect to filter just the way I want to view. On the other hand, I just splatted the whole output to sed and I’m done.

Seductive power tools, these sed and awk. And despite what my co-workers think, I’m not even all that good with them … but I know how to look up 80% of what I need from sed1line and awk1line.txt, and I glean the rest from Google or from scripts I’ve already written. (Every once in a while, I’m really clever. The other days, I just refer back to what I did then.) Most of it is actually knowing regular expressions from studying man regexp in my youth. Now I’d probably just use txt2regex instead.

Tips, Troubleshooting, Unix, CodeAugust 14, 2009 9:33 am

A good place to start my workday of Expect scripting: The 8 most common errors:

  1. Have you mis-spelt a variable name?
  2. Have you mis-spelt a command name?
  3. Have you used a $ when setting a variable, or left one off when using the contents of a variable?
  4. Do your brackets balance?
  5. Have you left spaces in the right places?
  6. Have you added a new line in the middle of a command and forgotten the \ on the end of all lines except the last?
  7. Do you need \ protection on anything?
  8. Have you got () {} or [] mixed up?

I’ll keep those in mind.

Macintosh, Tips, Journal, CodeAugust 13, 2009 2:40 pm

When my USB thumb drive died, and then its brand new replacement followed it, I looked for an online alternative. I picked Syncplicity because I liked the screen shots: I knew exactly what I would see as a user. I used it to access live copies of my most-used files on both my work and home MacBook Pros. Well, Syncplicity decided to drop Mac support cold at the end of last month. I finally got around to uninstalling it, which was a pain because the directions disappeared out from under me.

If you need ‘em, those directions are:

1. Shut Syncplicity down
2. Delete /Applications/Syncplicity.app
3. Delete /Library/Contextual Menu Items/SyncpCMPlugin.plugin
4. Delete ~/Library/Application Support/Syncplicity
5. Restart your Mac

My first thought while reading those directions was:

1. osascript -e "tell application \"Syncplicity\" to quit"
2. rm -rf /Applications/Syncplicity.app
3. sudo rm -rf /Library/Contextual\ Menu\ Items/SyncpCMPlugin.plugin
4. rm -rf ~/Library/Application\ Support/Syncplicity
5. osascript -e "tell application \"Finder\" to restart"

so I decided to script it. Since the first and last steps were AppleScript commands, I dabbled around and came up with an AppleScript application to uninstall Syncplicity for the Mac (download).

For alternatives, I picked Dropbox (SugarSync is similar), but if you lean towards backups too then Mozy, iDrive, and rsync.net are also strong contenders.

The last step, after making sure all my files are current and local, is to cancel my account at Syncplicity. Time to move on!

Macintosh, Tips, CodeAugust 11, 2009 3:31 pm

From the inimitable Daring Fireball, how to see if an application is running with AppleScript:

tell application \"System Events\"
    count (every process whose name is \"BBEdit\")
end tell

I modified that into tell application "System Events" to set syncpRunning to count (every process whose name is "Syncplicity") for my purposes.