02 Feb 2023Another one of those things I need to often do in zsh and never remember the syntax.
I had a multiline string (as returned by fzf
) and wanted to convert it into a single line, with spaces instead of spaces.
This was the right formula: selection=("${(f)selection}")
02 Feb 2023I had a large chunk of text (output from another command), and needed to perform a search and replace on it. I usually do that either with zsh builting ${var:gs/x/y/}
syntax or sed
, but this time my pattern was spread on several lines.
Multi-line with --null-data
The trick here is to use sed ---null-data
to make it operate on the full text instead of on invidual lines. Technically, it now considers a "line" to end with a NUL
character, and not a \n
anymore.
Improve readability with --regexp-extended
To improve readability of my regexps, I also used --regexp-extended
which allows me to write capturing groups without having to escape them ((.*)
instead of \(.*\)
).
Non-greedy
sed
does not have a non-greedy mode, which means it will always capture the largest group it can.
The way to work around that involves writing a slightly more complex regexp by specifically defining the one character we don't want to capture.
For example with the string foo_bar_baz
, I might want to find the part before the first _
by doing ^(.*)_
. This will actually return foo_bar
because it's greedy.
The way to work around that is to use ^([^_]*)_
instead. This can be read as: capture everything that is not a _
from the start until you met a _
.
17 Jan 2023This is one of those transformations I know how to do with node, ruby, or even the command line, but that I always have to refer to Stack Overflow when attempting to do it with zsh
.
Hopefully, writing this blog post (and referring to it later) will help me remember how to do it.
To split a string variables by a delimiter, one can use the (${(@s/X/)variableName})
syntax.
- The wrapping
()
means that the resulting variable will be treated as an array - The
${}
interpolation syntax allow passing specific modifiers - The
(@s/X/)
modifier means to split it by the X
character.
To split by the /
character, you can use an alternate syntax like (@s:/:)
instead. The character following the @s
is part of the zsh syntax parsing, and the character between them will be your actual delimiter.
15 Jan 2023zsh
comes bundled with variable modifier to alter filepaths and extract the relevant parts.
Given the following code, we can display $filepath
in a lot of different ways:
mkdir -p /tmp/subdir
cd /tmp
local filepath=./subdir/file.zsh
| Name | Output | Modifier | Mnemonic | | --------- | ---------------------- | ----------------- | ------------------ | | Absolute | /tmp/subdir/file.zsh
| ${filepath:a}
| a
bsolute | | Basename | file.zsh
| ${filepath:t}
| t
ail | | Filename | file
| ${filepath:t:r}
| t
ail r
est | | Extension | zsh
| ${filepath:e}
| e
xtension | | Dirpath | /tmp/subdir
| ${filepath:a:h}
| a
bsolute h
ead |
For clarity
t
ail is everything after the last /
h
ead is everything before the last /
e
xtension is everything after the last .
r
est is everything before the last .
Other goodies
${~filepath}
will expand ~
to their full path, while ${filepath/#$HOME/\~}
will use ~
instead of home path:c
ommand gives you the executable path of a command (a bit like which
):q
for quoting, :U
for unquoting, :x
for quoting individual words:l
for lowercase, :u
for uppercase:2:10
takes a substring from 2
to 10
- Those modifiers can be applied directly to glob patterns (
src/**/*.zsh(:t:r)
)
14 Jan 2023zsh
has two modifiers (${:-}
and ${:=}
) to handle fallback for empty values.
echo ${ahead:-0}
will display the variable $ahead
, or display 0
if the variable is empty.
echo ${ahead:=0}
(note the :=
instead of :-
) will assign 0
to the variable $ahead
, and then display it.
They are pretty similar, and in that example the result is the same. But with :=
, the variable will still be set to 0
afterward, while with :-
it's a one-off thing.