Last active
March 14, 2021 18:58
-
-
Save ernstki/b782cc7f2a29ec01c1f4355f2dd312cc to your computer and use it in GitHub Desktop.
Determine if a given version of Bash has a specific 'shopt' option using all available 'bash' image tags on Docker Hub
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# old version that may not work anymore; see docker-image-test.sh instead | |
# | |
# source: http://www.googlinux.com/list-all-tags-of-docker-image/index.html | |
# maybe API documentation? | |
# shellcheck disable=SC2086 | |
REGISTRY=https://registry.hub.docker.com/v2/repositories/library | |
[[ $(uname -s) == Darwin ]] && SORT=gsort || SORT=sort | |
tags=() | |
image=${1:-bash} | |
option=${2:-direxpand} | |
# set CLEANUP=1 in the environment to remove all downloaded images | |
CLEANUP=${CLEANUP:-} | |
if test -t 1; then | |
YEP=$(tput bold)$(tput setaf 2) # bold, green | |
NOPE=$(tput bold)$(tput setaf 1) # bold, red | |
RESET=$(tput sgr0) | |
fi | |
readarray -t tags < <( | |
for (( i=1; $?==0; i++ )); do | |
curl -sL $REGISTRY/$image/tags/?page=$i \ | |
| jq -r '.results[]["name"]' 2>&- | |
done \ | |
| grep -v devel \ | |
| $SORT -V | |
) | |
for tag in "${tags[@]}"; do | |
if docker run --rm bash:$tag bash -c "shopt | grep -q $option" 2>&-; then | |
echo "$YEP$tag$RESET: has '$option'" | |
else | |
echo "$NOPE$tag$RESET: doesn't have '$option'" | |
fi | |
(( CLEANUP )) && docker rmi bash:$tag 2>&- | |
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
## | |
## Test if a command passes with all tags for a specific Docker image; | |
## e.g., which versions of Bash support 'shopt -s direxpand' | |
## | |
## Requires at least Bash 4.x to for 'mapfile' / 'readarray'; the /bin/bash | |
## that ships with macOS is not sufficient for this, so the shebang line | |
## is set to use whatever 'bash' is available in your PATH, from MacPorts | |
## or Homebrew or whatever; other OSes probably won't have such an ancient | |
## version of Bash, and likely won't have any problem. | |
## | |
## Additionally requires 'curl' and 'jq' (https://stedolan.github.io/jq/) | |
## and requires GNU sort to be available as 'gsort' on non-Linux platforms | |
## (as it is with 'coreutils' installed from MacPorts); edit the script at | |
## line ~44 if yours is named something else. | |
## | |
## Author: Kevin Ernst <ernstki -at- mail.uc.edu> | |
## Date: 13 March 2021 | |
## License: MIT | |
## | |
## Hat tip: | |
## - http://www.googlinux.com/list-all-tags-of-docker-image/index.html | |
## | |
# shellcheck disable=SC2086 | |
# set TRACE=1 in the environment to trace execution | |
(( TRACE )) && set -x | |
# terminate on uninitialized variables | |
set -u | |
# set DOCKER_IMAGE_TEST_IMAGE in the environment or use -i / --image to use an | |
# image besides 'bash' (the default) | |
IMAGE=${DOCKER_IMAGE_TEST_IMAGE:-bash} | |
# set DOCKER_IMAGE_TEST_CLEANUP=1 in the environment (or use -c / --cleanup) | |
# to remove all downloaded images when we're done | |
CLEANUP=${DOCKER_IMAGE_TEST_CLEANUP:-} | |
# set SHELL in the environment to use something besides bash | |
IMAGE_SHELL=${DOCKER_IMAGE_TEST_SHELL:-bash} | |
ME=${BASH_SOURCE[0]##*/} | |
GIST='https://gist.github.com/ernstki/b782cc7f2a29ec01c1f4355f2dd312cc' | |
REGISTRY='https://registry.hub.docker.com/v2/repositories/library' | |
# update this if your GNU sort is called something else | |
if [[ $(uname -s) == Linux ]]; then SORT=sort; else SORT=gsort; fi | |
# programs required to be available | |
MANIFEST=( $SORT jq curl ) | |
# only use ANSI escapes if stdout is a terminal | |
if [[ -t 1 ]]; then | |
BOLD=$(tput bold) | |
UL=$(tput sgr 0 1) | |
YEP="$BOLD$(tput setaf 2)" # bold, green | |
NOPE="$BOLD$(tput setaf 1)" # bold, red | |
RESET=$(tput sgr0) | |
else | |
BOLD=; UL=; YEP=; NOPE=; RESET= | |
fi | |
ERROR="${NOPE}ERROR$RESET" | |
USAGE=" | |
$BOLD$ME$RESET - test a command with all tags for a specific Docker image | |
${UL}usage$RESET | |
$(basename $0) [-h|--help] [-c|--cleanup] [-i|--image IMG] cmd [args...] | |
${UL}where$RESET | |
-h, --help prints this help ;-) | |
-c, --cleanup removes downloaded Docker images when we're through | |
-i, --image IMG uses a different Docker image from the default ($IMAGE) | |
-n, --dry-run don't run anything, just print what would happen | |
-q, --quiet don't print progress; don't print commands to be run | |
Optionally, set DOCKER_IMAGE_TEST_IMAGE or DOCKER_IMAGE_TEST_CLEANUP in | |
the environment to get the same behavior as '-c' and '-i'; to use a shell | |
besides $IMAGE_SHELL to run the command, set DOCKER_IMAGE_TEST_SHELL. | |
${UL}problems?$RESET | |
Report bugs at $GIST | |
" | |
cleanup=$CLEANUP | |
image=$IMAGE | |
dryrun= | |
# default to printing tag fetch progress and command to be run for each image | |
verbose=1 | |
shell=$IMAGE_SHELL | |
tags=() | |
cmd=() | |
endofopts= | |
json=$(mktemp) | |
trap "\\rm '$json'" EXIT | |
# make sure all necessary programs are available | |
for passenger in "${MANIFEST[@]}"; do | |
if ! type "$passenger" >/dev/null 2>&1; then | |
echo "$ERROR: Missing required program '$passenger'." >&2 | |
exit 1 | |
fi | |
done | |
while (( $# )); do | |
case $1 in | |
-*) | |
# '--' signals end of options for this script to process itself | |
if [[ $1 == "--" ]]; then | |
endofopts=1; shift; continue | |
fi | |
if [[ $endofopts ]]; then | |
cmd+=("$1") | |
else | |
case $1 in | |
-h|--help) | |
echo "$USAGE" | |
exit | |
;; | |
-i|--image*) | |
if [[ $2 ]]; then | |
shift | |
image=$1 | |
elif [[ $1 =~ --image=(.*) ]]; then | |
image=${BASH_REMATCH[1]} | |
else | |
echo "$ERROR: -i / --image requires an argument" >&2 | |
exit 1 | |
fi | |
;; | |
-c|--cleanup) | |
cleanup=1 | |
;; | |
-n|--dryrun|--dry-run) | |
dryrun=1 | |
;; | |
-q|--quiet) | |
verbose= | |
;; | |
*) | |
cmd+=("$1") | |
;; | |
esac | |
fi | |
;; | |
*) | |
cmd+=("$1") | |
;; | |
esac | |
shift | |
done | |
if [[ -z ${cmd:-} ]]; then | |
echo "$USAGE" >&2 | |
echo "$ERROR: Expected (at least) a command to run." >&2 | |
exit 1 | |
fi | |
echo -n "Enumerating Docker registry tags for '$image'" >&2 | |
readarray -t tags < <( | |
for (( i=1; $?==0; i++ )); do | |
# I thought you /used/ to be able keep going until '?page=$i' query | |
# string returned a 404, but now you have to check .next in the | |
# returned JSON | |
curl -sL "$REGISTRY/$image/tags/?page=$i" > "$json" | |
echo -n . >&2 | |
jq -r '.results[]["name"]' "$json" | |
# returns non-zero if .next is null | |
jq -e .next "$json" >/dev/null 2>&1 | |
done \ | |
| grep -v devel \ | |
| $SORT -V | |
) | |
echo -e " found ${#tags[*]} tags." >&2 | |
(( verbose )) && echo >&2 | |
for tag in "${tags[@]}"; do | |
# otherwise you have to hammer Ctrl+C a bunch of times | |
trap break INT | |
if (( dryrun )); then | |
echo docker run --rm "$image:$tag" "$shell" -c "${cmd[*]}" >&2 | |
else | |
if ( | |
(( verbose )) && set -x; | |
docker run --rm "$image:$tag" "$shell" -c "${cmd[*]}" 2>/dev/null | |
); | |
then | |
echo "$YEP$tag$RESET: test '${cmd[*]}' passed" | |
else | |
echo "$NOPE$tag$RESET: test '${cmd[*]}' failed" | |
fi | |
(( verbose )) && echo | |
(( cleanup )) && docker rmi bash:$tag 2>&- | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's an example from https://unix.stackexchange.com/a/464650/278323…
When did you start being able to
as opposed to the "longhand" form using
eval
?The answer is Bash 4.4, which you can determine with:
Bash up to 4.0.x would either return an error (because associative arrays weren't supported), and up to 4.3.x would assign the string value of
$adef
to the numerical index${b[0]}
, yielding a result like this:Starting at 4.4, you'll see this (intended) result instead
The workaround using
eval
works on all Bashes 4.x and above, which have support for associative arrays. So if you need to support, say, Bash 4.2.something on CentOS 7, stick with theeval
form.