Speechcode.com https://speechcode.com/ Blog posts on Command-line by Arthur A. Gleckler en-US [email protected] (Arthur A. Gleckler) Backup Sampling https://speechcode.com/blog/backup-sampling https://speechcode.com/blog/backup-sampling <article class="post backup-sampling"><img class="hero" src="https://speechcode.com/blog/backup-sampling/backup-sampling.svg"> <div class="metadata"> <div class="date">Sat 8 Mar 2025</div> <div class="labels"><a href="https://speechcode.com/blog/label/command-line">command-line</a>, <a href="https://speechcode.com/blog/label/miscellaneous">miscellaneous</a></div></div> <div class="contents"> <p>Early in my career as a software engineer, I worked at a large database company. The company took data loss seriously. Their private data center contained computers of many types, including systems made by DEC, HP, IBM, SGI, Siemens, and Sun. They did backups constantly. Periodically, someone would transport a huge cart of metal boxes of tapes off site for storage. Seeing that cart in the elevator gave me confidence that my files were safe. At first.</p> <p>In the three years I worked there, I only submitted two tickets to restore files from backup. One time, the folks who ran the backup service reported that the backup had been corrupted. The other time, the files I wanted were missing from the backup. The elaborate backup system failed, and I had to reconstruct the files from scratch.</p> <p>I learned a lesson from that experience: <span class="pull-this-quote">Unless you test your backup, you haven't done a backup.</span> However, while I'd love to check every file in every backup against the original, that would make the backups take about twice as long, which would encourage me to do them less often. Instead, I compare a random sampling of the files. The more files in the sample, the more confident I can be that the backup is complete and not corrupt.</p> <p>I've written a shell script to automate this work. Here's how to use it to compare 1000 files chosen at random from the <code>~/papers/</code> directory to the corresponding backup files:</p> <pre>compare-random-sample \ /Users/arthur/papers/ \ /Volumes/MacintoshHD\ backup\ 20230916/ \ 1000</pre> <p>The output will list each file that has been compared, and whether it is identical to, different from, or missing from the backup. The last line shows how many mismatches were found.</p> <p>By default, the script ignores hidden directories and files as well as patterns from <code>.gitignore</code>. If you want to check those files, too, add the <code>--no-ignore</code> option.</p> <p>You can find <code>compare-random-sample</code> <a href="https://github.com/arthurgleckler/compare-random-sample">on Github</a>. I've tested it on Linux and MacOS. I hope you find it useful. But even if you do, please be sure to test your backups manually every once in a while.</p></div></article> Arthur A. Gleckler Sat, 8 Mar 2025 12:00:00 +0000 papers https://speechcode.com/blog/papers https://speechcode.com/blog/papers <article class="post papers"><img class="hero" src="https://speechcode.com/blog/papers/aim-349.png"> <div class="metadata"> <div class="date">Mon 27 May 2024</div> <div class="labels"><a href="https://speechcode.com/blog/label/command-line">command-line</a>, <a href="https://speechcode.com/blog/label/miscellaneous">miscellaneous</a>, <a href="https://speechcode.com/blog/label/scheme">scheme</a></div></div> <div class="contents"> <p>I left graduate school decades ago, but I still love to read academic papers. The field of computer science reinvents its wheels constantly, but academic literature is a great way to mine existing ideas and avoid that problem. It's a way to &quot;<a href="https://en.wikipedia.org/wiki/Standing_on_the_shoulders_of_giants">stand on the shoulders of giants</a>.&quot;</p> <p>For a long time, I maintained and carefully indexed a collection of actual printed papers. Once it reached the hundreds, that approach became too cumbersome. I ended up throwing away papers in order to avoid having too many, but often regretted doing that when some half-remembered idea popped up again in another context.</p> <p>Now I have a crude system that meets my needs. I keep notes on the most interesting papers using <a href="https://en.wikipedia.org/wiki/Org-mode">Org-mode</a> files, and I keep my collection in a Git repo in purely digital form. Every paper appears in the top-level directory, and there's a subdirectory <code>to-read/</code> for papers I haven't yet read. A little bit of automation helps, too. Now managing almost two thousand papers is no problem.</p> <p>Here's the Scheme program, <code>papers</code>, I use for adding new papers:</p> <pre>#!/usr/local/bin/chibi-scheme -r ;;;; -*- mode: scheme -*- ;;;; Expect environment variable &lt;p&gt; to name a directory that holds ;;;; all papers, e.g. &quot;papers/&quot;. A subdirectory &quot;to-read/&quot; that holds ;;;; unread papers must also exist. ;;;; Copy specified documents into &quot;$p/&quot; directory. ;;;; If &lt;--to-read&gt; is specified, copy them to &quot;$p/to-read/&quot;, too. ;;;; If &lt;--commit&gt; is specified, use Git to commit each of the new ;;;; documents. Each document will be committed separately. It is an ;;;; error if any other file is already staged. (import (chibi filesystem) (chibi pathname) (only (chibi process) exit process-&gt;output+error+status) (scheme process-context) (scheme small) (scheme write) (only (srfi 1) any remove) (srfi 98) (only (srfi 130) string-join string-prefix?) (srfi 166 base)) (define (echo . rest) (apply show #true rest) (show #true nl)) (define (echo-command command) (apply echo (list &quot;command: &quot; (string-join command &quot; &quot;)))) (define (usage program) (echo &quot;Usage: &quot; (path-strip-directory program) &quot; [--commit] [--to-read] &lt;pathname&gt; ...&quot; nl nl &quot;Environment variable p must be set to a directory for holding papers.&quot; nl &quot;It must have a subdirectory called \&quot;to-read/\&quot;.&quot; nl)) (define (run command) (let* ((out+err+status (process-&gt;output+error+status command)) (stdout (car out+err+status)) (stderr (cadr out+err+status)) (status (caddr out+err+status))) (unless (zero? status) (echo-command command) (write-string stderr) (write-string stdout) (exit 1)))) (define (run/assert command message) (unless (zero? (caddr (process-&gt;output+error+status command))) (echo-command command) (echo message) (exit 1))) (define (act document papers commit? to-read?) (let ((filename (path-strip-directory document))) (link-file document (path-resolve filename papers)) (when commit? (run `(&quot;git&quot; &quot;add&quot; ,filename))) (when to-read? (let ((place (path-resolve filename (path-resolve &quot;to-read&quot; papers)))) (symbolic-link-file (path-resolve filename &quot;..&quot;) place) (when commit? (run `(&quot;git&quot; &quot;add&quot; ,place))))) (when commit? (run `(&quot;git&quot; &quot;commit&quot; &quot;-m&quot; ,filename))))) (define (switch? string) (string-prefix? &quot;--&quot; string)) (define (main arguments) (let* ((program (car arguments)) (options (cdr arguments)) (papers (or (get-environment-variable &quot;p&quot;) (begin (usage program) (exit 1)))) (valid-switches '(&quot;--commit&quot; &quot;--help&quot; &quot;--to-read&quot;))) (cond ((member &quot;--help&quot; options) (usage program) (exit 0)) ((any (lambda (a) (and (switch? a) (not (member a valid-switches)))) options) (usage program) (exit 1))) (let* ((commit? (member &quot;--commit&quot; options)) (to-read? (member &quot;--to-read&quot; options)) (cwd (current-directory)) (documents (map (lambda (f) (path-resolve f cwd)) (remove switch? options)))) (when (null? documents) (usage program) (exit 1)) (change-directory papers) (when commit? (run/assert `(&quot;git&quot; &quot;diff&quot; &quot;--cached&quot; &quot;--quiet&quot;) &quot;Error: Files already staged.&quot;)) (for-each (lambda (f) (act f papers commit? to-read?)) documents))))</pre> <p>For example, to add one paper, including a copy in <code>to-read/</code>, commiting it to the repo:</p> <pre>papers --commit --to-read /tmp/aim-349.pdf</pre> <p>I use <a href="https://www.gnu.org/software/mit-scheme/">MIT Scheme</a> for most of my Scheme hacking, but Alex Shinn's <a href="https://synthcode.com/scheme/chibi/">Chibi Scheme</a> is wonderful for implementing this kind of tool. It's small, <a href="https://small.r7rs.org/">R7RS-Small</a>-compliant, and has many useful libraries. Thank you, Alex!</p> <p>Fixed on Wed 29 May 2024 to handle relative pathnames.</p> </div></article> Arthur A. Gleckler Mon, 27 May 2024 12:00:00 +0000