Swap function: move A to B and B to A

Function definition:

# move a to b and b to a
swap() {
    [[ "$#" -ne 2 ]] && { echo >&2 '2 paths must be supplied'; return 1; }
    { [[ -e "$1" ]] || [[ -e "$2" ]]; } || { echo >&2 'Neither file exists'; return 1; }
    [[ "$1" = "$2" ]] && { echo >&2 "Can't swap file to itself"; return 1; }
    if [[ -e "$1" ]] && [[ -e "$2" ]]; then
        [[ -e "$1.$$" ]] && { echo >&2 'Swap temp file already exists. Aborting'; return 1; }
        mv "$1" "$1.$$" &&
        mv "$2" "$1" &&
        mv "$1.$$" "$2"
    elif [[ -e "$1" ]]; then
        mv "$1" "$2"
    elif [[ -e "$2" ]]; then
        mv "$2" "$1"
    fi
}

Example usage:

Use case 1: toggling a file between two locations

% ls
custom.conf
% swap custom.conf custom.conf.bak
% ls
custom.conf.bak
% swap custom.conf custom.conf.bak
% ls
custom.conf

Use case 2: swapping two files around

% cat a
A contents
% cat b
B contents
% swap a b
% cat a
B contents
% cat b
A contents

Breakdown

  • [[ "$#" -ne 2 ]] && { echo >&2 '2 paths must be supplied'; return 1; }
    • Ensure there are 2 arguments to the swap function
  • { [[ -e "$1" ]] || [[ -e "$2" ]]; } || { echo >&2 'Neither file exists'; return 1; }
    • Ensure at least 1 argument exists
  • [[ "$1" = "$2" ]] && { echo >&2 "Can't swap file to itself"; return 1; }
    • Ensure both arguments are distinct; swapping a file to itself would be a no-op
  • if [[ -e "$1" ]] && [[ -e "$2" ]]; then
    • The case for swapping 2 currently-existing files. Do both files exist?
    • [[ -e "$1.$$" ]] && { echo >&2 'Swap temp file already exists. Aborting'; return 1; }
      • $$ expands to the pid of the current shell.
      • "$1.$$" is being used as a temporary location when swapping two files that exist.
      • An example of what "$1.$$" might be is "/etc/nginx/conf.d/site.conf.3941"
    • mv "$1" "$1.$$" &&
      • Move A to the temporary location
    • mv "$2" "$1" &&
      • Move B to the previous location of A
    • mv "$1.$$" "$2"
      • Move A from the temporary location to the previous location of B
  • elif [[ -e "$1" ]]; then
    • The case for moving A to B
    • mv "$1" "$2"
  • elif [[ -e "$2" ]]; then
    • The case for moving B to A
    • mv "$2" "$1"