223223 foo "${a[@]}"
224224 #+end_src
225225
226-
227226** Read lines from file
228227 The =read= command we used just above is part of the usual idiom to
229228 read a file line by line.
13531352 With this, you can find out how many matches happened in that
13541353 line. The example above filters lines which have 2 words from the
13551354 'foo|bar|baz' regex.
1356-
1357-
1358-
13591355*** Set operations
13601356There are lots of other "set level" operations you can perform on
13611357files/streams using basic unix tools.
@@ -1966,7 +1962,15 @@ coprocs would mess up your inline functions.
19661962 and if the user forgets any, the shell will barf.
19671963
19681964** Idempotent functions
1965+ Being able to call the same function multiple times even if you just
1966+ called it, without having to account for "oh, did this already happen
1967+ or not?" but instead have a mental model of "I need this to have
1968+ happened after this function call", and consider it done, is very
1969+ liberating from the code perspective.
19691970
1971+ Also, there are subgoals, like caching slow functions, downloads, etc.
1972+
1973+ *** nop subsequent calls
19701974 "My favourite shell scripting function definition technique:
19711975 idempotent functions by redefining the function as a noop inside
19721976 its body:
@@ -1980,6 +1984,64 @@ coprocs would mess up your inline functions.
19801984 echo "This bit will only be executed on the first foo call"
19811985 }
19821986 #+end_src
1987+ *** Download once
1988+ A function might be sideffecty, as in "download files". These sort of
1989+ cases, you want to download the file only if you haven't downloaded it
1990+ yet.
1991+
1992+ =cache= here gets a cache "id" where it'll store the results of =http=
1993+ and next time, when called with the same param, it will reuse the saved version.
1994+
1995+ This pattern applies to many kinds of functions, so with slight
1996+ modifications you can adapt it.
1997+
1998+ #+begin_src bash
1999+ cache() {
2000+ local path=$CACHE_DIR/$1
2001+ [[ -f $path ]] && cat $path && return 0
2002+
2003+ shift
2004+ $@ | tee $path
2005+
2006+ return 1
2007+ }
2008+
2009+ get_repos() {
2010+ local url="https://api.github.com/search/repositories?q=user:$USER&access_token=$GITHUB_TOKEN&per_page=100&page=$1"
2011+ cache $USER-repos-$1 http $url
2012+ }
2013+ #+end_src
2014+
2015+ *** memoize with ttl, with ttl
2016+ What if you want to cache for some time?
2017+
2018+ The tricks here:
2019+
2020+ - Use a file as a sentinel for the cache.
2021+ - Create a file, set it's modification date to now()+12hours
2022+ - check with `-nt` (newer than) and a temporary file (to have a
2023+ =now()= value in "file" format).
2024+
2025+ #+begin_src bash
2026+ hmacache="/tmp/.hmacache"
2027+ is_recent_cache() {
2028+ [ "$hmacache" -nt <(echo 1) ]
2029+ }
2030+ update_cache() {
2031+ touch -m -t "$(date -v+12H +"%Y%m%d%H%M.%S")" "$hmacache"
2032+ }
2033+
2034+ main() {
2035+ #...
2036+ if ! is_recent_cache ; then
2037+ if ! curl -s https://hello.ts.net > /dev/null; then
2038+ die "Tailscale must be connected to start Harbormaster"
2039+ fi
2040+ # other slow checks
2041+ update_cache
2042+ fi
2043+ }
2044+ #+end_src
19832045
19842046* Debugging
19852047** adding =bash= to a script to debug
0 commit comments