viernes, 10 de febrero de 2017

Zsh global aliases for command chaining

(The motivation for this post is my recent discovery of oilshell, a very promising new shell.  The author has been posting regularly for some time, and he has quite insightful ideas. Some of them I had them too and I didn't know they conformed to some defined concepts (I discovered Bernstein chaining two weeks ago). Whatever.... here it goes, as a way to open more discussion topics)

There's a small zsh trick that is unmatched by any other shell. It's maybe just syntax sugar, but it enables some pretty neat tricks.

The feature is global aliases (or "alias -g").

They are alias substituted anywhere on a line. Global aliases can be used to abbreviate frequently-typed usernames, hostnames, etc.

I think of them as some kind of reader macro, that applies substitutions to strings at read time, so there's no need to substitute a full command by another full command.

alias -g

 

The simplest case is to make an alias for a bunch of flags for a command you use many times (Also look at zsh globbing to see other examples of 'simple' alias -g).

alias -g GLWEEK=' --since=1.week.ago --author=kidd'

Given this ("git last week"), we can use it in different contexts. In this example, the only useful ones that come to my mind are "git log" and "gitk", but to get the feeling of what it does internally, you can 'echo GLWEEK' and you'll get the flags printed.  It allows for a lot of composability, and Bernstein chaining because everything concatenates and tries to apply.

Piping through

 

Another way to see it is as a way to Forthify your shell a bit more with pipes. This is how I use the feature the most:

If you make aliases expand to pipes (left, right or both), you can make many unix commands compose in a seamless way. Under the hood, you know that the connexion happens through stdin/stdout, but visually it eliminates most of the clutter.

Let's see some examples:

alias -g T='| tail'
alias -g T1='T -1'
alias -g X='| xclip '
alias -g G='| grep '
alias -g GV='| grep -v '
alias -g DM='| dmenu '

Let's see a few ways we can compose this:

ls -ltr G foo GV bar T1 X
evince $(ls *pdf DM)

With some training, it's very easy to see that this will copy the last file that matches 'foo' but doesn't match 'bar', or it will open the selected pdf file.

XARGS

 

There are some commands that do not accept arguments from stdin, but they require them to be as command line arguments. in those cases, one starts doing things like:

mplayer $(ls *(mpg|mp4|avi) DM )

Which works, but it breaks the pipe flow.  For that, we can use xargs (as we saw a few posts ago), and we can use, again, another global alias:

alias -g XA='| xargs '

now we can do "ls *(mpg|mp4|avi) DM XA mplayer" .

FUNCTIONS 

 

Here's an example of a uniq function on steroids. it accepts a field number to uniquify, and the rows do not have to be sorted to work.

function uc () {
    awk -F" " "!_[\$$1]++"
}

alias -g UC=' | uc '
alias -g P1='| awk "{print \$1}"'

Let's see how to use this one: For the sake of the example, let's pretend we want to know the users that have some process running in my machine.

ps -axuf GV USER UC 1 P1

Of course, this is the same as typing the vanilla commands yourself, but IMO, it makes quite a lot of difference on the experimentation easiness.

There are some more fancy tricks, but we'll leave them for some future post.

The whole point of this is that some features allow for greater composability, and let you build your ad-hoc tools in a much easier way than others.  Oilshell has plans to have '{', '}' and '[', ']' as operators, so you can have block like syntax (maybe the semantics of tcl's quoting would be enough?).  Anyway, it's great that the shell space is active and there are interesting things happening. I'll keep oil in my radar :)