Howardism Musings from my Awakening Dementia
My collected thoughts flamed by hubris
Home PageSend Comment

zsh: More Word Expansion

As I was mentioned in last week's tip, Zsh can expand the ! character into an event designator, substituting a previous command into your current command… but that wasn't the tip.

The tip was that you could take part of a previous command (called a /word designator/) instead of the entire command.

In this week's tip, I mention that these word designators can themselves take a modifier for even more wacky hijinx.

Dirnames, Basenames and Extensions

Let's suppose that instead of getting the entire path of a previous argument, we just wanted the directory, or the filename or just the extension. Just out these examples:

$ cp src/reasoned_schemer/chapter4.clj ../../org/clojure/logic_002.clj
$ ls !cp:2:h          ## HEAD --> Expand to:
$ ls ../../org/clojure 
$ touch !cp:2:t       ## TAIL --> Expand to:
$ touch logic_002.clj
$ echo "!cp:1:e"      ## EXT  --> Expand to:
$ echo "clj"
$ echo !cp:1:r        ## ROOT --> Expand to:
$ echo src/reasoned_schemer/chapter4

Why yes, these do work inside double quotes (but not single quotes). The modifiers are also cumulative, like:

$ echo !cp:1:h:r      ## HEAD+ROOT --> Expand to:
$ echo chapter4
$ ls !cp:2:h:a        ## HEAD+ABS  --> Expand to:
$ ls /Users/howard/Dropbox/org/clojure

What is the :a modifier? Read on…

Getting the Absolute Path

The :a modifier takes the file path and converts it to an absolute path, expanding relative ".." paths in the process. As you can expect, it doesn't take into consideration any directory changes, as in:

$ cp src/reasoned_schemer/chapter4.clj ../../org/clojure/logic_002.clj
$ cd ..
$ ls !cp:2:a         ## ABS --> Expand to:
$ ls /Users/habrams/org/clojure/logic_002.clj
ls: cannot access /Users/howard/org/clojure/logic_002.clj: No such file or directory

The :A modifier attempts to resolve symbolic links.

What did I just run?

You can use the :c modifier to get the file path to the command that you ran. Why is that important? Probably helpful when something goes wrong, for instance, let's suppose I ran this cp command, and I didn't want the Extended Attributes (EAs) or resource forks on my Mac system:

$ cp -X nothing.clj /tmp
cp: invalid option -- 'X'

What is going on with my Mac!? I try to diagnose what I previously did:

$ !cp:0:c             ## CMD --> Expand to:
$ /opt/local/bin/gcp

Oh! I'm not using the cp command that came with my system, but picked up a GNU library version… that explains the difference.

That wasn't too much of a contrived example, was it?

Now we're getting ridiculous

The :s modifier is a regular expression conversion… which is either overkill or freakin' crazy (depending on who you ask):

$ ls !cp:2:s/clojure/scheme/      ## --> Expand to:
$ ls ../../org/scheme/logic_002.clj

Quote that

I assume that if you have typed in a command before, you can just take the parameters as is in subsequent calls, however, you can add single quotes or remove them:

$ echo '$PATH'
$ echo !!:1:Q      ## UNQUOTE --> Expand to:
$ echo $PATH
$ echo !!:1:q      ##   QUOTE --> Expand to:
$ echo '$PATH'


Zsh offers other modifiers, like :l (for lowercasing the parameter) and :u (for uppercasing it), but these seem pretty straight-forward.

The :s can be quite complex, so be careful. Next week, I think I'll dive into process subsitution, but we'll see if I get distracted when I go to Clojure/West.

Tell others about this article:
Click here to submit this page to Stumble It