#!/usr/bin/env bash set -eu -o pipefail usage() { name=$(basename "$0") # Note: convention used is max line length of 80 chars. echo $"Usage: $name [OPTIONS...] [DOCKER_OPTIONS...] [--] IMAGE [ARGS...] $name - Runs a given container image in a given directory (defaults to current directory) OPTIONS -s, --src SRC_PATH The directory to mount inside the container. Will be mounted under DEST_PATH. Default: \$PWD -d, --dest DEST_PATH The location where the SRC_PATH directory will be mounted inside the remote container. Will be set as the working directory inside the container. Default: \$PWD -- Indicates the end of the options for $name. DOCKER OPTIONS Any options that unrecognized by 'docker-here' will be forwarded to the 'docker-run' command. (!) Syntax must be: '--my-option=my-value' - equal signs are required for any option that take an argument. Examples: $name --privileged alpine ls $name --volume=/tmp:/mount-point alpine ls /mount-point $name --pull=never my-local-image ls POSITIONAL ARGUMENTS IMAGE The container image to run. ARGS The arguments to pass to 'docker run'. ENVIRONMENT VARIABLES DOCKER_EXE_PATH The path to the 'docker' command executable (or similar such as podman). Default behavior: automatically detects the command to use. EXAMPLES $name alpine ls . $name alpine sh -uxc 'ls -l \"\$PWD\" ; cat /etc/os-release' $name alpine --src ~/Downloads -- ls -l . $name alpine --dest /foo -- ls -l /foo $name alpine:latest@sha256:d34db33f[...] -- ls -l /foo $name --volume=/tmp:/mount-point alpline ls -l /mount-point" } get_docker() { supported_commands=( docker podman ) for cmd in "${supported_commands[@]}"; do if command -v "$cmd"; then # If found, stop searching. # # 'command -v' will print the command path which acts # as our return value. return 0 fi done echo "Couldn't find a supported 'docker' command in PATH." >&2 echo "Supported commands: ${supported_commands[*]}" >&2 echo "Make sure docker is installed." >&2 exit 1 } # Peeks at the next argument, exits with 1 if unset. peek_next() { if [[ -z "${2-}" ]]; then echo "Missing argument for $1" >&2 usage >&2 exit 1 fi } image= docker_run_args=( ) src_path="$PWD" dest_path= while [[ $# -gt 0 ]]; do case "$1" in -s | --src) peek_next "$@" src_path="$2" shift 2 ;; -d | --dest) peek_next "$@" dest_path="$2" shift 2 ;; -h | --help) usage exit 0 ;; --) # Stop parsing options here shift # consume the '--' if [[ -n "$image" ]]; then # If the image was already provided, then don't consume the # next argument. break elif [[ -z "${1-}" ]]; then # We expect the user to instantly provide the image after '--' echo "Missing required argument: IMAGE" >&2 usage >&2 exit 1 fi # Consume the image and stop processing options. image="$1" shift break ;; --*) docker_run_args+=("$1") shift ;; *) image="$1" shift # Stop processing, a positional argument was passed thus # we assume everything else goes to 'docker run' break ;; esac done if [[ -z "$image" ]]; then echo "Missing argument: IMAGE" >&2 usage >&2 exit 1 fi # Retrieve a docker cli command (e.g., /usr/bin/docker, or /usr/bin/podman) if [[ -n "${DOCKER_EXE_PATH-}" ]]; then # Use the user provided executable path docker="$DOCKER_EXE_PATH" else # Auto-detect 'docker' executable path docker=$(get_docker) fi # If we are running inside a TTY, then add '-ti' to the 'docker run' arguments. if [[ -t 0 ]]; then docker_run_args+=( -ti ) fi # Use full path to avoid relative directories (may not play well # with some docker CLIs otherwise) src_path=$(readlink -f "$src_path") # Set the default destination path if unset. # Use the same as the source path for user-friendliness. if [[ -z "$dest_path" ]]; then dest_path="$src_path" fi "$docker" run --rm \ "${docker_run_args[@]}" \ -v "$src_path":"$dest_path" \ --workdir "$dest_path" \ "$image" \ "$@"