06 Feb 2023I had a commandOptions string variable that I wanted to use as arguments to fzf. But commandOptions had all kind of spaces and quoted strings in it and I had a hard time passing it as a set of distinct arguments and not one long string argument.
The best solution I found was to actually display this commandOptions in a multiline format, where each line was an option. Then, using the ${(f)commandOptions} modifier to read it.
get_options
#!/usr/bin/env zsh
local commandOptions=()
commandOptions+="--disabled"
commandOptions+="--delimiter= "
commandOptions+="--with-nth=3"
commandOptions+="--bind=change:reload:sleep 0.1;${sourceBinary} {q} || true"
for line in $commandOptions; do
echo $line
done
use_options
#!/usr/bin/env zsh
local myOptions="$(get_options)"
local selection="$(fzf ${(f)myOptions})"
03 Feb 2023Given an array with a lot of values, I want to keep only one occurence of each value. I want to make those values unique.
The (u) modifier
local myArray=(a b c d a b b)
echo ${(u)myArray} # a b c d
Applied on an array, the (u) (for unique) deduplicates the array.
The typeset -aU definition
Somehow, I didn't work in my case and I'm still unsure why, so I found another way.
By defining my variable as an array with unique values from the get go, I don't need to deduplicate it manually, it will automatically refuse duplicate values.
typeset -aU myArray
local myArray=(a b c d a b b)
echo ${(u)myArray} # a b c d
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.