Why a Bash Implementation of realpath? ¬

2012-04-03

Shortly after my recent post of a realpath implementation in bash my friend David Kendal suggested a C implementation would be preferable. I have to admit, it’s far simpler code, it wraps around the actual realpath() so no work to make it functionally equivalent, and it’ll be immensely faster (although I haven’t benchmarked it). See his example implementation. I really can’t agree more.

That said, I do see use for the bash implementation.

Why bash?

Most of my bash scripts are are exercises to keep my bash-fu honed, so this one certainly started that way and did so as a part of tools-osx. While not a modern, object-oriented language, I find bash to be there for me on nearly every platform I use (excluding the obvious like pre-Mac OS X, Newton OS, etc.) without needing to build or install anything else. There are feature differences between versions, but they are minor.

It’s very much in the spirit of UNIX and forces you to use the other command-line tools at your disposal, in conjunction with each other. So, I find it a great way to master more command-line tools and also to become a better command-line user. The more I learn in bash the more I’m able to do efficiently on the command-line, esp. complex tasks. As a server admin, this is immensely helpful. And, the server admin in me is probably why I find it greatly comforting to not have to install or maintain another language and its tools.

It’s a relatively minimalist language and lacks a lot of niceties, but I consider it a bit of a survivalist language at this point. It’s no Perl, Python, or Ruby and it may be frustrating to work with arrays or return anything but integers, but if you can master it you’ll be able to be tossed into any random server and come out alive.

So, Why realpath in bash?

The real benefit for a realpath implementation in bash is the portability without the need to compile for new platforms. Simply copy to the appropriate directory and you’re done. This is ideal for situations where you cannot guarantee a compiler will be available (the use-case for tools-osx) or cannot have a compiler installed (one I commonly encounter in my admin work due to various security requirements, but also one people with shared hosts often run into).

I was also recently contacted by someone who is using my realpath implementation in part of the process for launching a Java app on Linux and Mac OS X, so in this case it’s ideal to keep everything extremely portable with only one codebase and no build process required. Exactly the kind of thing I had in mind.

In Conclusion

So, the bash implementation of realpath has a couple of use-cases, mostly if you’re shooting for no compile process for installation. If you’ve already got a compile process, you’ll probably be better off with the C implementation for performance and to reduce your codebase. If you’re Linux-only, or platforms where you already have readlink available, just use readlink. Naturally, this is all with bash scripting in mind, most higher languages have a built-in realpath function.

A realpath Implementation in Bash ¬

2012-02-19

I was recently informed of an issue with the in-development version of trash (one of the utilities in tools-osx) that required using an absolute path internally instead of a relative path. In most languages one can just run a path through realpath() to get the absolute path for a relative path, but bash (which trash was developed in) doesn’t have an equivalent. Many people suggest readlink, but it’s generally only included in Linux distributions, so BSD-based operating systems (incl. Mac OS X) are a bit out of luck.

Fortunately, it’s quite easy to emulate using pwd, but there is a bit of extra work that must be done. I found a good, portable example, but it still wasn’t quite up to my standards. I’ve simplified & improved it and the result is as follows (last updated 2012-03-26):

function realpath()
{
	local success=true
	local path="$1"

	# make sure the string isn't empty as that implies something in further logic
	if [ -z "$path" ]; then
		success=false
	else
		# start with the file name (sans the trailing slash)
		path="${path%/}"

		# get the basename of the file (ignoring '.' & '..', because they're really part of the path)
		local file_basename="${path##*/}"
		if [[ ( "$file_basename" = "." ) || ( "$file_basename" = ".." ) ]]; then
			file_basename=""
		fi

		# extracts the directory component of the full path, if it's empty then assume '/'
		local directory="${path%$file_basename}"
		if [ -z "$directory" ]; then
			directory='/'
		fi

		# attempt to change to the directory
		if ! cd "$directory" &>/dev/null ; then
			success=false
		fi

		if $success; then
			# does the filename exist?
			if [[ ( -n "$file_basename" ) && ( ! -e "$file_basename" ) ]]; then
				success=false
			fi

			# get the absolute path of the current directory & change back to previous directory
			local abs_path="$(pwd -P)"
			cd "-" &>/dev/null

			# Append base filename to absolute path
			if [ "${abs_path}" = "/" ]; then
				abs_path="${abs_path}${file_basename}"
			else
				abs_path="${abs_path}/${file_basename}"
			fi

			# output the absolute path
			echo "$abs_path"
		fi
	fi

	$success
}

Here’s a simple example of its usage:

relative_path="~/Desktop/"
absolute_path="$(realpath "$relative_path")"
if [ $? -eq 0 ]; then
	echo "absolute_path = $absolute_path"
else
	echo "'$relative_path' does not exist!"
fi

I have also tossed together a realpath tool which wraps around this implementation if you would like to install it for use in multiple scripts/tools. Feel free to download it from the development page or get the source code on GitHub. It’s released under a BSD license.

Environment Variables in NewtonScript and NEWT/0 ¬

2012-01-22

To expand upon my previous post demonstrating how to access command line arguments (ARGV) in NewtonScript in NEWT/0, I’ll demonstrate how to access environment variables. Command line arguments are only the beginning for writing command line tools as you often need to access environment variables as well. In some situations, like running a script as a CGI in your favorite HTTP server software, environment variables are really your only way to receive input from the outside world.

After digging through the NEWT/0 source code again, it appeared that I could again use GetGlobalVar(), but this time with the '_ENV_ object literal, but it didn’t seem to have anything useful in it, certainly not any environment variables. So, back to the source code I went and discovered that NEWT/0 implements a custom global function GetEnv(), like the getenv() equivalent in C, accepts a string for the environment variable name and returns the value stored in said environment variable.

To get the PATH environment variable, for example:

#!/usr/bin/env newt

Print("PATH:" && GetEnv("PATH") && "\n");

Or a basic “hello world” CGI that also echoes the query string:

#!/usr/local/bin/newt

Print("Content-Type: text/plain\n\n");

Print("Hello world!\n\n");
Print("QUERY_STRING:" && GetEnv("QUERY_STRING") && "\n");

I must thank NEWT/0’s developer, Makoto Nukui, for including all this functionality!

Command Line Arguments in NewtonScript and NEWT/0 ¬

2012-01-21

Still being a daily Newton user as well as a developer, I’ve always wanted to learn NewtonScript. I’ve got a PowerMac 9500 that’s been configured for Newton OS development for quite some time and I’ve got a couple projects in mind, but I just never seem to get around it.

I’ve also had NEWT/0, an open source, cross-platform NewtonScript interpreter, installed for a while. Since it can run NewtonScript scripts from the command line like any other interpreter, I decided I’d like to know if it was possible to get the command line arguments from NewtonScript so I could eventually write some local scripts to get myself familiar with the syntax & functions (therefore less of a learning curve when I finally get around to writing some actual Newton packages). After a point in the right direction from Matthias Melcher and a lot of digging through the NEWT/0 source code, I finally figured out how to do so.

Here’s a quick NewtonScript script that can be run through NEWT/0 to print out the command line arguments you pass to it:

#!/usr/bin/env newt

Print("'_ARGV_ exists? ");
if GlobalVarExists('_ARGV_) then Print("Yes.\n") else Print("No.\n");

foreach slot, value in GetGlobalVar('_ARGV_) do Print(slot && ":"
&& value && "\n");

I doubt there are many, if any, out there that are interested in doing this, but—who knows?—maybe somebody else will start scripting in NewtonScript too. (Expect to see some more NewtonScript-related news around here in the future.)

Twitter Statuses Badge 0.7.1 Released ¬

2011-08-16

Or, this should’ve been done nine months ago.

When I released v0.7 of my Twitter Statuses JavaScript Badge back in November of last year, it was right around the time that Twitter was rolling out their new ‘snowflake’ status ID generator which was going to have some implications for languages such as JavaScript. Unfortunately, I didn’t become aware of this until I started seeing random issues with status IDs a couple months later. More unfortunately, I’m only getting around to applying the minor tweak to fix this, and a minor issue with applying the ‘last’ class when there’s only one LI element, now.

Without further ado, I present v0.7.1! Please accept my apologies for sitting on this issue for so long and let me know if you have any questions, comments, or feature requests. Also, the source code is on GitHub.

FYI – I started developing a major update back in March, including a few frequently requested features. Hopefully I’ll get that polished up sometime this year.

tools-osx 2011-06-02 ¬

2011-06-02

I’ve updated my collection of Mac OS X command line tools with improvements to the trash tool:

  • trash can now list trash contents across all volumes (with a disk usage total) as well as empty the trash (including a secure empty option; both emptying operations require confirmation).

Note: Obviously, the new version of trash is now destructive. I have tested and use this myself, but I cannot guarantee it won’t securely erase your entire hard drive if it encounters some odd edge case and I cannot be held responsible for that. So, use at your own risk and keep backups!

Go download them all (clipcat, eject, swuser, and trash) or get the source code on github!

sde_newsletter v0.5 ¬

2011-03-28

I’ve got a very minor update to the sde_newsletter Textpattern plug-in. v0.5 word-wraps HTML & text content in message/part bodies to 78 characters (if possible) to prevent hard wrapping at 998 characters which tends to badly break HTML content.

The sde_newsletter plug-in helps build HTML, text, or multipart email messages from Textpattern pages (although, technically, you can use any web-facing source HTML & text). It’s straightforward and very powerful when paired with newsletter/mailing list software.

Update: I discovered a minor bug parsing page title from HTML if the TITLE element contains line breaks. This has been resolved in v0.5.1.