#!/bin/bash # # fbopt version 0.4.1 # # Written in 2018 by Franco Masotti/frnmst # # To the extent possible under law, the author(s) have dedicated all # copyright and related and neighboring rights to this software to the public # domain worldwide. This software is distributed without any warranty. # # You should have received a copy of the CC0 Public Domain Dedication along # with this software. If not, see # . # # # See also https://frnmst.gitlab.io/notes/my-bash-option-parsing-template.html # show_help() { cat <<-EOF Usage: qvm [OPTION] Trivial management of 64 bit virtual machines with qemu. Only a single option is accepted. By default, the backup vhd is run. Mandatory arguments to long options are mandatory for short options too. Options: -a, --attach connect via SSH -b, --backup backup the vhd -c, --create create a new vhd -d, --delete delete the vhd backup -h, --help print this help -i, --install install the image on a vhd -m, --mkdir-shared create a shared directory -n, --nox run vm without graphical output -o, --orig run from the original vhd -r, --remote connect to a remote instance of QVM -v, --vnc use VNC -x, --run run the vm Exit status: 0 if OK, 1 if an error occurred. CC0 License Written in 2018 by Franco Masotti/frnmst EOF } # A function that prints the variable name and value of all # the flags enabled by the user. This is useful to check that # all the flags are correct, as kind of a dry run. show_flags() { local flags="${*}" for flag in ${flags}; do printf "%s='%s'\n" "${flag}" "${!flag}" done } getopt_error() { local program_name="${0}" printf "%s\n" "Try '"${program_name}" --help' for more information" } 1>&2 2>&- main() { # Create a new array from the reference of the input one. # See https://stackoverflow.com/questions/1063347/passing-arrays-as-parameters-in-bash # See some comments below. declare -a argc=("${!1}") # Set the options. local getopt_short_options='abcdhimnorvVx' local getopt_long_options='attach,backup,create,delete,help,install,run,\ remote,vnc,mkdir-shared,nox,print-flags,version' # Set the default values for the flags. local attach='false' local backup='false' local create='false' local delete='false' local install='false' local nox='false' local origin='false' local remote='false' local run='false' local mkdir_shared='false' local version='false' local vnc='false' local program_name="${0}" opts="$(getopt \ --name "${program_name}" \ --shell bash \ --options "${getopt_short_options}" \ --longoptions "${getopt_long_options}" \ -- \ "${argc[@]}")" getopt_retval=${?} # Check that getopt works and that some kind of argument # is passed to the script. This is "quotation hell". a="'"${argc[@]}"'" { [ ${getopt_retval} -ne 0 ] || [ -z "${a}" ]; } && getopt_error && return 1 eval set -- "${opts}" # Option parsing. while [ "${1}" != '--' ]; do case "${1}" in -a | --attach ) attach='true' ;; -b | --backup ) backup='true' ;; -c | --create ) create='true' ;; -d | --delete ) delete='true' ;; -h | --help ) help='true' ;; -i | --install ) install='true' ;; -m | --mkdir-shared ) shared='true' ;; -n | --nox ) nox='nox' ;; -o | --origin ) origin='origin' ;; --print-flags ) print_flags='true' ;; -r | --remote ) remote='remote' ;; -v | --vnc ) vnc='vnc' ;; -x | --run ) run='true' ;; esac # Iterate through all arguments. shift 1 done shift 1 # Everything else after '--' is an argument. argc="${*}" # Check that the flags that must be non empty are actually not empty. # A user might infact circumvent getopt's mechanisms like this # ./program -flag '' # This can also be done inside the option parser loop but to avoid nestings # I prefer it done here. # NOOP. [ "${print_flags}" = 'true' ] \ && show_flags \ 'attach' \ 'backup' \ 'create' \ 'delete' \ 'help' \ 'install' \ 'mkdir_shared' \ 'nox' \ 'origin' \ 'run' \ 'remote' \ 'version' \ 'vnc' \ && return 0 [ "${help}" = 'true' ] && show_help && return 0 # Override values of optional parameters. # NOOP. # From now on you should call a function or an external program # using the values of the flag variables. local place='' local display='' check_dependencies || exit ${?} { [ "${attach}" = 'false' ] && [ "${backup}" = 'false' ] \ && [ "${create}" = 'false' ] && [ "${delete}" = 'false' ] \ && [ "${install}" = 'false' ] && [ "${run}" = 'false' ]; } \ && printf "%s\n" 'an action needs to be specified' 1>&2 2>&- \ && return 1 if [ "${attach}" = 'true' ]; then [ "${remote}" = 'remote' ] && place='remote' || place='local' [ "${vnc}" = 'vnc' ] && display='vnc' || display='' { attach "${place}" "${display}"; return ${?}; } fi [ "${backup}" = 'true' ] && { backup; return ${?}; } [ "${create}" = 'true' ] && { create; return ${?}; } [ "${delete}" = 'true' ] && { delete "${origin}"; return ${?}; } [ "${install}" = 'true' ] && { installs "${vnc}"; return ${?}; } if [ "${run}" = 'true' ]; then if [ "${remote}" = 'remote' ]; then # Start remote instance. [ "${vnc}" = 'vnc' ] && display='vnc' { run_remote_instance "${display}"; return ${?}; } else [ "${vnc}" = 'vnc' ] && display='vnc' [ "${nox}" = 'nox' ] && display='none' [ -z "${display}" ] && display='gtk' { run "${display}" "${origin}"; return ${?}; } fi fi [ "${mkdir_shared}" = 'true' ] && { mkdir_shared; return ${?}; } } # Test dependencies and versions. # getopt must return 4 to be fully compatible. See getopt's manual. which bash getopt 1>/dev/null 2>/dev/null && { getopt -T; [ ${?} -eq 4 ]; } # Get and pass argc to the main function. # All this work with an array must be done to preserve # quotations for arguments that have whitespaces. # See https://lists.debian.org/debian-user/2007/12/msg01244.html declare -a opts=() for opt in "${@}"; do opts=("${opts[@]}" "${opt}") done main 'opts[@]'