commit a085fb9de10fa959884be0b794143056a5f0fa1e
Author: Alex Balgavy <alex@balgavy.eu>
Date: Sat, 13 Mar 2021 18:29:27 +0100
Initial commit
Diffstat:
A | .gitignore | | | 34 | ++++++++++++++++++++++++++++++++++ |
A | Makefile | | | 19 | +++++++++++++++++++ |
A | README.md | | | 12 | ++++++++++++ |
A | _vbox | | | 52 | ++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | vbox | | | 184 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | vbox.1.man | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
6 files changed, 371 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,34 @@
+
+# Created by https://www.toptal.com/developers/gitignore/api/macos
+# Edit at https://www.toptal.com/developers/gitignore?templates=macos
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# End of https://www.toptal.com/developers/gitignore/api/macos
+
diff --git a/Makefile b/Makefile
@@ -0,0 +1,19 @@
+prefix=/usr/local
+datarootdir=$(prefix)/share
+datadir=$(datarootdir)
+exec_prefix=$(prefix)
+bindir=$(exec_prefix)/bin
+mandir=$(datarootdir)/man
+man1dir=$(mandir)/man1
+
+all:
+ @echo "Targets: install, uninstall, man"
+
+install: vbox vbox.1.man
+ cp vbox $(bindir)/
+ cp vbox.1.man $(man1dir)/vbox.1
+ @echo "If you want Zsh completion, copy the _vbox file to your Zsh completions directory."
+
+uninstall:
+ rm $(bindir)/vbox
+ rm $(man1dir)/vbox.1
diff --git a/README.md b/README.md
@@ -0,0 +1,12 @@
+# Vbox: a wrapper for VBoxManage and other commands
+This script aims to make manipulating Virtualbox machines on the command line more user-friendly.
+
+## Installation
+### macOS
+Via Homebrew (`brew install thezeroalpha/formulae/vbox`), via the methods below.
+
+### Other
+You have two options:
+
+* Install using make: run `make install` in the root of this repository
+* Install manually: download the repo, then copy the `vbox` script to a directory that's in your `PATH`, copy the manpage, and add the `_vbox` file if you use Zsh and want completions.
diff --git a/_vbox b/_vbox
@@ -0,0 +1,52 @@
+#compdef vbox
+_vboxmachines() {
+ VBoxManage list vms | egrep -o '^"[^"]+"' 2>/dev/null | sed -e 's|"||g' | while read machine; do
+ _wanted 'machine' expl 'machine' compadd $machine
+ done
+}
+_vbox() {
+ local -a arguments
+ arguments=(
+ 'start:start a VM'
+ 'stop:stop a VM'
+ 'open:open a VM, starting it if necessary'
+ 'pause:pause a VM'
+ 'suspend:suspend a VM'
+ 'resume:resume a paused VM'
+ 'ls:list known VMs'
+ 'info:get information about a VM'
+ 'status:get status of a VM'
+ 'running:list currently running VMs'
+ 'share:share a local folder'
+ 'unshare:unshare a local folder'
+ 'sharetmp:temporarily share a local folder'
+ 'shared:list shared folders for a VM'
+ )
+ local context state line expl
+ local -A opt_args
+ _arguments '*:: :->subcmds' && return 0
+
+ if (( CURRENT == 1 )); then
+ _describe -t commands "vbox commands" arguments -V1
+ return
+ fi
+
+ case "$words[1]" in
+ share|sharetmp)
+ _arguments \
+ ':hostpath:_files -/' \
+ :machine:_vboxmachines \
+ ':name: :'
+ ;;
+ unshare)
+ _arguments \
+ ':hostpath:_files -/' \
+ :machine:_vboxmachines
+ ;;
+ start|stop|open|status|info|shared|pause|suspend|resume)
+ _arguments \
+ :machine:_vboxmachines
+ ;;
+ esac
+ return 1
+}
diff --git a/vbox b/vbox
@@ -0,0 +1,184 @@
+#!/bin/sh
+die() {
+ echo "$1" >&2
+ exit 1
+}
+command -v VBoxManage >/dev/null 2>&1 || die "VBoxManage cannot be found, please install it."
+command -v gstat >/dev/null 2>&1 && statcmd="gstat"
+start() {
+ [ $# -eq 1 ] || die "Only one argument: VM name"
+ VBoxManage startvm "$1" --type headless
+}
+
+stop_vm() {
+ [ $# -eq 1 ] || die "Only one argument: VM name"
+ VBoxManage controlvm "$1" acpipowerbutton
+}
+
+open() {
+ [ $# -eq 1 ] || die "Only one argument: VM name"
+ VBoxManage startvm "$1" --type separate
+}
+
+pause() {
+ [ $# -eq 1 ] || die "Only one argument: VM name"
+ VBoxManage controlvm "$1" pause
+}
+suspend_vm() {
+ [ $# -eq 1 ] || die "Only one argument: VM name"
+ VBoxManage controlvm "$1" savestate
+}
+resume() {
+ [ $# -eq 1 ] || die "Only one argument: VM name"
+ vminfo="$(VBoxManage showvminfo "$1")"
+ if [ "$(printf '%s' "$vminfo" | awk '/^State/ { print $2 }')" = "saved" ]; then
+ snapshots="$(printf '%s' "$vminfo" | awk '/^Snapshot folder/ { print $3 }')"
+ snapfile="$(find "$snapshots" -name "*.sav" -exec "${statcmd:-stat}" -c "%y %n" {} + | sort -r | head -n1 | cut -d " " -f 4-)"
+
+ [ -n "$snapfile" ] \
+ && echo "VM suspended, resuming from saved state..." \
+ && VBoxManage startvm "$1" --type headless
+ else
+ echo "VM paused, resuming..."
+ VBoxManage controlvm "$1" resume
+ fi
+}
+list() {
+ VBoxManage list vms
+}
+
+running() {
+ VBoxManage list runningvms
+}
+
+info() {
+ VBoxManage showvminfo "$1"
+}
+
+status() {
+ VBoxManage showvminfo "$1" | awk -F' +' '/^State/ { print $2 }'
+}
+
+# share /folder/path vmname /mount/point
+share() {
+ [ $# -eq 3 ] || die "Not enough arguments"
+ [ -d "$1" ] || die "$1 is not a directory or does not exist"
+ if ! VBoxManage sharedfolder add "$2" --name "$(basename "$(realpath "$1")")" --hostpath "$(realpath "$1")" --automount --auto-mount-point "$3"; then
+ echo "Could not add shared folder, machine is probably running."
+ echo "Stop the machine to add a permanent folder, or use sharetmp to add a transient folder."
+ fi
+}
+
+unshare() {
+ [ $# -eq 2 ] || die "Not enough arguments"
+ [ -d "$1" ] || die "$1 is not a directory or does not exist"
+ VBoxManage sharedfolder remove "$2" --name "$(basename "$(realpath "$1")")" --transient
+}
+
+sharetmp() {
+ [ $# -eq 3 ] || die "Not enough arguments"
+ [ -d "$1" ] || die "$1 is not a directory or does not exist"
+ VBoxManage sharedfolder add "$2" --name "$(basename "$(realpath "$1")")" --hostpath "$(realpath "$1")" --automount --auto-mount-point "$3" --transient
+}
+
+shared() {
+ [ $# -eq 1 ] || die "Not enough arguments"
+ VBoxManage showvminfo "$1" | grep '^Name: .*Host path: '
+}
+PARAMS=""
+while [ $(($#)) -ne 0 ]; do
+ case "$1" in
+ -h|--help)
+ echo "Usage:"
+ echo "start vmname start a VM"
+ echo "stop vmname stop a VM"
+ echo "open vmname open a VM, starting it if necessary"
+ echo "list, ls list VMs"
+ echo "running list running VMs"
+ echo "share /local/path vmname /mount/point share a local folder"
+ echo "unshare /local/path vmname unshare a local folder"
+ echo "sharetmp /local/path vmname /mount/point temporarily share a local folder"
+ echo "shared vmname list shared folders"
+ echo "pause vmname pause a running VM"
+ echo "resume vmname resume a paused VM"
+ echo "info vmname get information about a VM"
+ echo "status vmname print a VM's status"
+ exit 0
+ ;;
+ --) # end arg parsing
+ shift
+ break
+ ;;
+ -*) # unsupported flags
+ echo "Unsupported flag $1" >&2
+ exit 1
+ ;;
+ *) # preserve positional arguments
+ PARAMS="$PARAMS $1"
+ shift
+ ;;
+ esac
+done
+eval set -- "$PARAMS"
+
+[ $# -ge 1 ] || die "Not enough arguments provided."
+
+case "$1" in
+ "start")
+ shift;
+ start "$@";
+ ;;
+ "stop")
+ shift;
+ stop_vm "$@";
+ ;;
+ "open")
+ shift;
+ open "$@";
+ ;;
+ "ls"|"list")
+ list;
+ ;;
+ "running")
+ running;
+ ;;
+ "share")
+ shift;
+ share "$@";
+ ;;
+ "unshare")
+ shift;
+ unshare "$@";
+ ;;
+ "sharetmp")
+ shift;
+ sharetmp "$@";
+ ;;
+ "shared")
+ shift;
+ shared "$@";
+ ;;
+ "info")
+ shift;
+ info "$@";
+ ;;
+ "status")
+ shift;
+ status "$@";
+ ;;
+ "pause")
+ shift;
+ pause "$@";
+ ;;
+ "suspend")
+ shift;
+ suspend_vm "$@";
+ ;;
+ "resume")
+ shift;
+ resume "$@";
+ ;;
+ *)
+ die "Unsupported command $1";
+ ;;
+esac
diff --git a/vbox.1.man b/vbox.1.man
@@ -0,0 +1,70 @@
+.TH VBOX 1 "13 March 2021"
+.SH NAME
+vbox - Manipulate VirtualBox machines
+
+.SH SYNOPSIS
+\fBvbox\fP [ -h ]
+\fIcommand\fP
+
+.SH DESCRIPTION
+\fBvbox\fP lets you manipulate (start, stop, pause, suspend) VirtualBox VMs, and change shared folders.
+
+.SS Options
+.TP
+\fB-h\fP
+Get help.
+
+.SS Commands
+.TP
+\fBstart vm-name\fP
+Start the VM \fIvm-name\fP (in headless mode)
+
+.TP
+\fBstop vm-name\fP
+Stop the VM \fIvm-name\fP
+
+.TP
+\fBopen vm-name\fP
+Open the GUI for the VM \fIvm-name\fP
+
+.TP
+\fBlist\fP, \fBls\fP
+List VMs.
+
+.TP
+\fBrunning\fP
+List running VMs
+
+.TP
+\fBshare /local/path vm-name /mount/point\fP
+Share the local folder \fI/local/path\fP to VM \fIvm-name\fP at guest mountpoint \fI/mount/point\fP
+
+.TP
+\fBunshare /local/path vm-name\fP
+Unshare the local folder \fI/local/path\fP from VM \fIvm-name\fP.
+
+.TP
+\fBshared vm-name\fP
+List the folders shared with \fIvm-name\fP.
+
+.TP
+\fBpause vm-name\fP
+Pause the VM \fIvm-name\fP.
+
+.TP
+\fBresume vm-name\fP
+Resume the VM \fIvm-name\fP.
+
+.TP
+\fBinfo vm-name\fP
+Get information about the VM \fIvm-name\fP.
+
+.TP
+\fBstatus vm-name\fP
+Print the status of the VM \fIvm-name\fP.
+
+.SH "SEE ALSO"
+VBoxManage(1),
+.UR https://\:www.virtualbox.org/\:manual/\:ch08.html
+VirtualBox VBoxManage documentation
+.UE