1 byte
a
a
is not (normally) a defined command in zsh
, so why is it useful? Because it generates an error message (zsh: command not found: a
). Errors are a key part of zsh
golfing, because it (and other languages in the Bourne shell family) is unique among practical programming languages in their error handling - almost all errors are ignored by default, which is great for golfing. Also, since errors are so easily to generate, they are also essentially the best way to implement conditionals:
2 bytes
&&
The &&
operator chains two commands together, but only runs the second if the first succeeds. There is also ||
, which does the opposite. They are pretty much the shortest way to implement conditional expressions, because if you can make your command generate an error in some circumstances, then you can use &&
instead of a bulky if
statement.
3 bytes
>$1
This is probably one of the most used trigraphs in my zsh
answers, but what does it do?
Well, the parts in isolation are $1
is the first command-line argument, and >
redirects a command to a file. But there is no command? Ah, but when no command is provided, zsh
uses the value of the NULLCMD
environment variable, or cat
by default. Therefore, it cat
s all of stdin
to a file named the first command-line argument. stdin
is generally not the optimal input method, and therefore I don't use it often, so it doesn't generally matter what goes into the file, but it matters that the file is created.[1]
Why does it matter that we create a file? Because the shortest way to do string pattern-matching in zsh
is with so-called "globbing". A glob pattern-matches to a file. I've given an overview of globbing syntax here, but it's generally the easiest and shortest way to check if two strings are equal, or check for substrings or match all sorts of patterns (especially with the --extendedglob
option). You can even create patterns dynamically, like I used in my Is it a lobster number? answer.
[1] If stdin
is really needed, you can use :>$1
which pipes a no-op command, or enable the --shnullcmd
option which changes that cat
default to a no-op
4 bytes
echo
The echo
builtin prints a string, like echo hello
. Pretty simple, right? But we can actually do 2 bytes better, with the <<<
syntax:
<<<hello
This is called "here-string" redirection, and it's equivalent to doing echo hello | command
. Like before, there is no command, however, so it's passed to cat
(see NULLCMD
above) and hence to stdout
.
The <<<
syntax has some limitations, like:
- it only expects a single word, so some expansion/word splitting techniques don't work
- the expression on the right is evaluated in a subshell, so assigning variables there won't take effect outside the command
- it's not a true command, so it can't be aliased and can't always be used in some complex statements
- if you want to print only a newline, a bare
echo
is one byte shorter than <<<""
...but it can be chained without command separators, like <<<a<<<b
, because multiple redirections are going to cat
(thanks to multIOs). This is particurlarly useful in eval
"loops" (see 7 bytes)
5 bytes
set -y
Unlike most shells, zsh does not perform field splitting by default, so variable expansions almost never need to be quoted. The -y
option re-enables such splitting, which is useful in some circumstances.
zsh
in fact has a plethora of command-line options, most of which can also be set dynamically with set
, setopt
, or unsetopt
; some more interesting options include:
-e
: errors are no longer ignored, and the shell exits immediately if one happens. If you can generate errors in some circumstances, this can act like a logical AND over the program which is useful for decision-problems
-n
: disables all command execution. Who knows why...
--extendedglob
: enables a whole load of extra syntax goodies for globbing
-F
: globbing is disabled. This is useful if you don't need to do any pattern matching but want to include unquoted special characters like *
. Particularly useful in ascii-art
--forcefloat
: changes division in arithemtic expansions to always produce a float
, even if both operands are integers
--cprecedences
: changes operator precedence of bitwise operators in arithmetic expansions. By default they are tighter-binding than even exponentiation, and this makes them exactly as they are in C[2]
You can find the full documentation for all of these in man zshoptions
.
[2] although this still isn't the most sensible, as comparison operators are tighter than bitwise AND/XOR/OR, in what may go down in history as Dennis Ritchie's greatest mistake
6 bytes
hash -d
While not so useful in code-golf, but this provides a mechanism very handy for interactive command-prompt use of zsh
. You may know that ~
refers to the home directory; well now you can define more directory aliases of the form ~a
. For example, hash -d a=/usr/local
means that echo ~a/bin/cat
will print /usr/local/bin/cat
.
It might be useful in restricted-source, but I haven't actually found a use for it yet outside my dotfiles.
7 bytes
{1..$1}
{1..$1}
constructs a range from 1
to the input, and expands them as separate words. This alone is not too interesting, but if you put some more string around it, like x{1..$1}y
, then it can create a range with the string, like x1y x2y x3y ...
. If you replace x
and y
with partial snippets of zsh
code, and then pass them to the eval
builtin (which evaluates strings as zsh
code), you can construct ugly-looking loops, which are often shorter than using for
.
8 bytes
${a/w/}
The ${...}
construct introduces parameter expansion. In the above example we are doing a text replacement on variable $a
, and removing any w
characters from its contents. Zsh has a pretty reasonable regex engine. Parameter expansion also does tricky things with arrays and expansion flags. Example
9 bytes
alias A=B
alias
defines token aliases (in the sense of C pre-processor macros), so in the example above, A
will be replaced with B
in parsing. If you're using a particular construct often, this can shorten it. For example, to output a lot of stuff, you can do[4]:
alias A=repeat\ 1000000
alias B='A A A A'
alias C='B B B B'
C C C C echo hi
With a little syntactic trickery, you can even alias operators:
alias X='&&'
(true)X echo hi
# => hi
(false)X echo hi
# no output
[3] Actually, for normal commands alone, it's shorter to define a function: A()command $@;
[4] This is by far non-optimal for the most finite output, but what ever
10 bytes
emulate sh
zsh has a built-in command to make its behaviour much more closely mirror the POSIX shell standard (and it can also emulate csh
and ksh
). I don't think it will ever have a use in code-golf, except maybe cops-and-robbers or polyglot, but it's another reason that zsh
should be the only shell installed on your system.
Factoid
Who needs awk
, sed
, or half of the other core unix utils when you can write simple Zsh code like the following from the Zsh User Guide?
This chapter will appeal above all to people who are excited by the fact that
print ${array[(r)${(l.${#${(O@)array//?/X}[1]}..?.)}]
prints out the longest element of the array $array
[...] This is for advanced students only (nutcases, if you ask me).