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.
13 Jan 2023To search and replace with zsh
, there are two ways.
With ${var//XXX/YYY}
This will replace all occurences of XXX
in $var
with YYY
.
Note that you can interpolate variables inside of XXX
, so ${var//${input}/YYY}
with input="foo"
will replace foo
with YYY
in $var
.
You can use only one /
instead of //
to only replace the first occurence.
With ${var:gs/XXX/YYY}
The :s/XXX/YYY
modifier is the basic syntax to replace XXX
with YYY
.
It does not allow for interpolating variables. You need to replace s
with gs
to make it a global search and replace.
The only advantage over the other syntax in my opinion is that you can swap the delimiter character (/
) with any other character you like. So if your patterns are heavy on /
, you can swap them for _
for example for a more readable format, like ${var:gs_/_#}
to replace all /
with #
.
12 Jan 2023Sometimes, you need to download a whole website locally. And for that, HTTrack is the best tool I ever found.
Sure, you could use wget
and some recursion to get what you want, but HTTrack solves all that for you.
Reasons you might need to download a whole website: - You need to browse it when you don't have an internet connection (on the go, with low data usage, in a train, etc) - You want to run scripts on the content, and it's faster with a local copy than a remote one - You want to make a copy of a website that might disappear