Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Pro-protip: don't use echo.

  # panic STRING-FORMAT [...]
  #
  # write printf-formatted message to stderr, prepending
  # script name, and exit failure
  panic() {
    printf "%s: %.0s${1}\n" "${0##*/}" "$@" >&2
    exit 1
  }
Prepending the script name is immensely helpful when diagnostic messages are mixed on stderr. It's also standard style for Unix utilities in general. The BSD extensions err(3) and warn(3), provided by all modern Unix libc implementations and commonly use by shell utilities, prepend the program name.

Another common style guideline is to include context for the diagnostic, often prepended. For example,

  wget --quiet --output-document /usr/local/bin/mybinary "$download_url" \
    || panic '%s: unable to download' "$download_url"


I'll use what I can read. I can't parse that at a glance, and won't use it.

The real pro tip is: don't use bash if it's not trivial to read


It's not Bash. I purposefully stick to POSIX shell constructs because memorizing all the various Bash (and ksh and Zsh) extensions and their oddball semantics is too much trouble. By sticking to POSIX shell it's easier to learn and remember actual shell programming semantics--parameter expansion, positional parameters, pathname globbing, etc. You have to understand those things, anyhow, if you actually care to understand the language; they happen whether you know it or not, and they're fundamental to the language.

Plus, the POSIX specification for the shell is significantly more concise and clear than the manuals for Bash, Zsh, etc. See Shell & Utilities -> Shell Command Language at https://pubs.opengroup.org/onlinepubs/9699919799/ I know exactly where to go if I can't remember the difference between ${foo:-X} vs ${foo+Y}.


Pro-protip tip: Add $0 but don't do pattern expansion. Worst case it's just slightly more verbose output. Best case, it's simpler to understand & maintain, and showing the path to your script will help unfamiliar people locate it quicker for debugging. (& I think it looks cleaner)

Also, don't run a command that directly overwrites a file unless you know it's going to succeed. A temp file lets you atomically replace the file only if the download succeeded. And a cleanup trap helps clean this up in the event of errors. Finally, you can die on unset variables or errors (cleanup still works) and you can enable tracing if environment variable DEBUG=1 was set.

  #!/usr/bin/env sh
  set -eu
  [ "${DEBUG:-0}" = "1" ] && set -x
  
  cleanup() { rm -f /usr/local/bin/mybinary.tmp ; } ; trap cleanup EXIT
  panic() { printf "$0: Error: %s\n" "$1" >&2; exit 1; }
  
  mkdir -p /usr/local/bin
  [ -e /usr/local/bin/mybinary ] || \
    wget -q -O /usr/local/bin/mybinary.tmp "$url" \
      || panic "wget was unable to download '$url'"
  mv -f /usr/local/bin/mybinary.tmp /usr/local/bin/mybinary


why is printf preferable to echo here other than formatting?


Echo is defined differently in different shells ("not portable"). Also it can have arguments which breaks things sometimes:

    echo Option -n invalid # OK
    echo -n is not valid # Uh oh
    printf '%s ' -n is not valid # Always okay
Generally you should use printf because of its consistency. For a simpler example of OP's:

    panic() { printf '%s\n' "${0}: ${*}"; exit 1; }
Is fine. I don't see the need to pass a format string to your function. Just call:

    panic "${download_url}: unable to download"




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: