Bash shell syntax goodies and gotchas

In my time bash scripting, I've come across a number of CLI goodies and gotchas, so I've decided to compile a post with a list of neat examples. These may be about individual packages or specific to the shell.

  • pdflatex -- the order of arguments matters :( For instance, I've written a bash script that's cron-scheduled to re-compile my resumé each morning. But, you must specify flags and options before the file name you're aiming to compile.
    pdflatex -var-value="$out" -output-directory="$misc" "$location"
    Otherwise, it fails.
  • Watch out for rm'ing against filesystems as such --
    read -r -p "Give me a file to delete: " var
    rm -r "$var/"
    If $var does not contain anything, then this could destroy your machine :P Instead, use
    rm -r "${var:?}/"
    For example, on my own machine --
    root@ideapad:~# read -r -p "This: " that; echo "${that:?}/"
    -bash: that: parameter null or not set
    root@ideapad:~# read -r -p "This: " that; echo "${that:?}/"
    This: that
  • You can't just acquire an indexed array from grep --
    screens=$(tmux ls | grep -oP "^([^:]+)(?=:)")
    Now, screen[-1], say, doesn't just contain the last screen in the list, it contains the entire "array" (string). Instead, you need to use mapfile, for which I haven't found many good examples online; I've come up with the following, which seems to check out.
    mapfile -t screens < <(tmux ls | grep -oP "^([^:]+)(?=:)")
  • btrfs has one of the friendliest CLI I've ever used. I'll leave it up to you to find out why haha. I wish more CLIs were like this.
  • Bash aliases are a blessing. I've only recently began really taking advantage of them on my system, including writing a few functions to allow me to cycle through open tmux sessions and execute my MFU ZFS commands, like
    alias ta='tmux attach -t'
    alias ts='tmux switch -t'
    alias tl='tmux ls'
    alias tk='tmux kill-session -t'
    alias tsh="tmux display-message -p '#S'"
    alias tn='tmux new -s'
    alias td='tmux detach'
    alias zl='zfs list -o name,creation,refer,available,mountpoint,mounted,origin'
    alias zs='zpool status -vP'
    alias zi='zpool iostat -vP'
    I might also add that it's suggested these be avoided in GNU documentation, and the function alternative be used instead (I see no reason not to keep a ~/.bash_aliases tab going, however).
  • Variables set in the current scope aren't updated in pipes. As a quick example that should be easy enough to follow --
    # X=0; for i in {1..10}; do printf "i: %d\\tX: %d\\n" $i $X; (( X+=i )); done | column -t; printf "X: %d\\n" $X
    i:  1   X:  0
    i:  2   X:  1
    i:  3   X:  3
    i:  4   X:  6
    i:  5   X:  10
    i:  6   X:  15
    i:  7   X:  21
    i:  8   X:  28
    i:  9   X:  36
    i:  10  X:  45
    X: 0
    However, remove the pipe to column and
    # X=0; for i in {1..10}; do printf "i: %d\\tX: %d\\n" $i $X; (( X+=i )); done; printf "X: %d\\n" $X
    i: 1    X: 0
    i: 2    X: 1
    i: 3    X: 3
    i: 4    X: 6
    i: 5    X: 10
    i: 6    X: 15
    i: 7    X: 21
    i: 8    X: 28
    i: 9    X: 36
    i: 10   X: 45
    X: 55
    all of a sudden X contains the droid we were looking for. I've tried to find a way around this, but haven't found anything beyond simply adding another loop (if you have something though, feel free to comment!).

If you have any others you'd like to add, feel free to comment! I'll continually update this list as time goes on.