Skip to content

Docker CLI Script

Sometimes while working on the command line, you want to take a quick look inside a running container, then get on with other things without much distraction.

Using “docker ps” is the usual way to start this process and is simple, but when working with Kubernetes (particularly without kubectl), cleaning up the output of “docker ps” to find the right ID became irritating.

Of course, kubectl exists, and ameliorates this significantly, but I wanted to take the guesswork/checking out of the process when working with multiple setups and get myself into the container I am interested in without delay, using only docker.

So this started out as a basic way to clean up the “docker ps” output for my use cases while working with Kubernetes, but has gradually expanded (as they do) to become my preferred way to briefly pull up the cli of a container for a look, even on systems just running docker alone.

For example, if I want to check the contents or permission of a mounted volume in, say, WordPress, I can issue the command; dcs word

The command line comes straight up. I can stay focused on my plans, complete the task, and get on with things without delay. It pays for itself.

Though I call it “dcs” in my ecosystem, you can rename it at your convenience without modifying anything else for functionality. However, you should probably also update the “–help” section to suit.


Full script

#!/bin/sh
set -e
# "pod" is used throughout the vars. 
# It's just easer than "container" or abbreviations thereof

option_output(){
# ${1} passed in from main, not cli. We already know it starts with "--".
  case "${1}" in
    --help) cat <<HELPTEXT

Quick container console startup
  dcs takes a single option.  

  "dcs --help" provides this help screen
  "dcs --list" tries to list only your userspace containers
               (Some may not have a terminal, so YMMV)

  "dcs <substring>" will try to put you in a unique matching container.
    When there are multiple matching containers they will be listed with highlighting
    With only two matching containers you are given a choice.
    Substring matches on unique container name and/or ID substring.
    Try not to use single-character strings.
    Prioritizes /bin/bash in the container. Fallback to /bin/sh.

HELPTEXT
    ;;
    --list) list_containers
    ;;
    *) echo "Option ${1} is not supported" 
    ;;
  esac
}

get_pod_list(){
  # The check for whether docker is available is already done in main.
  # Belt and suspenders, even though stakes are low.
  # Produces a table of 12 digit ID, image name, container name
  # Depending on what you are running the exclusions list can be adjusted.
  if [ -n "${docker_bin}" ]; then # Sanity check.
    "${docker_bin}" ps | \
      awk '!/CONTAINER|fleetagent|ingress|rancher|cattle|kube/ {print $1"_"$2"_"$(NF)}' |\
        cut -d"_" -f1,2,4 | sed 's/_/\ /g' |\
        awk '{printf "%-15s%-35s%-35s\n", $1, $2, $3}'
  else
    error_feedback no_docker # Should never happen
  fi
}

list_header(){
  printf "\033[93m%-15s%-35s%-35s\033[0m\n" "ID" "IMAGE NAME" "CONTAINER NAME"
}

list_containers(){
  # For presentation
  printf "\033[96m%s\033[0m\n" "Running user containers:"; 
  list_header
  get_pod_list
}

error_feedback(){
  case ${1} in
    no_name)
      printf "\033[93m%s\n%s\n\033[0m" \
             "You did not supply the container name as the first parameter" \
             "(Or you are lazily getting the list)"
      list_containers
    ;;
    no_unique) 
      printf "\033[96m%s %s\033[0m\n" \
             "The following list matches your string:" \
             "'${req_pod_string}'"
      list_header
      ##  We like grep color highlighting.
      echo "${match_list}" | grep --color -- "${req_pod_string}"
      printf "\033[91m%s\033[0m\n" \
             "The string you used is not unique, try being more specific"
    ;;
    no_pod)
      list_containers
      printf "\033[91m%s %s\n\033[0m" \
             "There are no matches for the supplied string" \
             "'${req_pod_string}'"
    ;;
    invalid_id) 
      printf "%s %s\n" "An ID with invalid length was seen." \
                       "Something unexpected happened"
    ;;
    no_docker)
      printf "\033[91m%s\033[0m\n" \
             "This script requires docker, which does not seem to be installed"
    ;;
    *) printf "%s\n" "Something went wrong"; list_containers
    ;;
  esac
}

## MAIN ##
# Checks
docker_bin="$(command -v docker)" # Used later
if [ -z "${docker_bin}" ]; then
  error_feedback no_docker # Exit with error message no docker
  exit 1
fi

# No name, ID or option.
if [ -z "${1}" ]; then
  error_feedback no_name; exit 1 # Message and list then exit
fi

# We have docker and a string. Lets see what the string is for.
if [ "${1%"${1#??}"}" = "--" ]; then # Option starts with "--"..
  option_output "${1}"; exit 0
fi

# Treat as string/substring of a name or ID. Following vars are reused later
req_pod_string="${1}" # Take only the first option, ignore anything after space
# Following grep handles possible expected fail case with "true"
# Prepending search string with "--" to handle when string begins with "-"
match_list="$(echo "$(get_pod_list)" | grep -- "${req_pod_string}" || true )"
possible_id="$(echo "${match_list}" | cut -d " " -f1)" # Needs echo here and above
id_length=12 # Docker IDs are 12 chars long. This may change in the future.
id_length_double=$((1+2*${id_length}))
possible_id_length="${#possible_id}"

# Create rangegaps in the subsequent switch/case. Probably never used.
if [ "${possible_id_length}" -gt 0 ] \
  && [ "${possible_id_length}" -lt "${id_length}" ]; then
    error_feedback invalid_id
    exit 1
elif [ "${possible_id_length}" -gt "${id_length}" ] \
  && [ "${possible_id_length}" -lt "${id_length_double}" ]; then
    error_feedback invalid_id
    exit 1
fi

case "${possible_id_length}" in
  0) error_feedback no_pod; exit 1
  ;;
  # case: Passed tests and has single ID length, assume it is a valid ID.
  ${id_length})
    # So go into the container. I sometimes prefer a clear screen, you may not.
  #  clear
    pod_name="$(echo "${match_list}" | awk '{print $3}')"
    printf "\n\033[93m%s %s\033[0m\n" "Connecting to" "${pod_name}"
    printf "\033[93m%s\033[0m\n" "(control-d to exit)" # Notify on entry
 
    # Set up cli for the matching container - bash if available, sh otherwise.
    # We give it a nice PS1 prompt if possible.
    connection_script='
      export PS1="\033[93m\h \033[92m\s \033[95m\u \033[96m[\w]\033[0m\$\040"
      bash_bin="$(command -v bash)"
      sh_bin="$(command -v sh)"
      if [ -n "${bash_bin}" ]; then
        "${bash_bin}"
      else
        "${sh_bin}"
      fi
    '
    # Enter the container
    "${docker_bin}" exec -it "${possible_id}" /bin/sh  -c "${connection_script}"
    printf "\n\033[93m%s %s\033[0m\n" "Exiting" "${pod_name}" # Notify on exit
    exit 0	
  ;;

  # case: Passed tests and matches [2x ID length + 1]. Make it an easy choice.
  # Specified ID is passed back into the script.
  ${id_length_double}) 
    error_feedback no_unique
    first_option_line="$(echo "${match_list}" | head -1 | sed 's/\ \ */\ /g')"
    printf "\n\033[93m%s \033[96m%s \033[93m%s\033[0m" \
           "Selecting"\
           "${first_option_line}"\
           "[Y/n]: "
    read -r select_first # n|N, or anything else for yes

    # Recurse back into this script definitively with selected full ID.
    script_myname="${0}" # Not assuming this script will be called "dcs"
    case "${select_first}" in
      n|N) # specific no
        second_option_id="$(echo "${match_list}" | tail -1 | cut -d " " -f1)"
        "${script_myname}" "${second_option_id}"
        exit 0 # Assumed success. "set -e" takes care of exit on failure though.
      ;;
      *) # assumed yes
        first_option_id="$( echo "${first_option_line}" | cut -d " " -f1)"
        "${script_myname}" "${first_option_id}"; exit 0 # Assumed success
      ;;
      esac
  ;;

  *) # case: Catchall. ID length is more than double+1. Multiple (> 2) matches
    error_feedback no_unique; exit 1 # Just notify and exit with failure
  ;;
esac

Examples

Specify by ID

$ dcs --list
Running user containers:
ID             IMAGE NAME                         CONTAINER NAME                     
b1f97805ce10   searx/searx                        searx                              
67c50f1fbd73   qmcgaw/gluetun                     vpn-gluetun-usa                    
ec444ac4af84   qmcgaw/gluetun                     vpn-gluetun-overseas               
ee6daaa57386   qmcgaw/gluetun                     vpn-gluetun-au                     
d9191e86f544   qmcgaw/gluetun                     vpn-gluetun-au-searx               
4d7151ceabbe   tkrs/maxmind-geoipupdate           geoip                              
ade6a3e10fb3   bitnami/wordpress                  wordpress                          
92fdd9565c52   mariadb                            mariadb                            
9a7c48dcbe1f   linuxserver/nextcloud              nextcloud                          
b6da179fd580   onlyoffice/documentserver          onlyoffice          
$ dcs 9a7

Connecting to nextcloud
(control-d to exit)
nextcloud-7d5df7676c-gqktc bash root [/]$ 

An ID substring can be used to specify which container you wish to use. This is the same as using the docker command but with less typing.

Specify by name, single result

$ dcs word

Connecting to wordpress
(control-d to exit)
I have no name!@wordpress-7d69f4fc5-p6bmb:/$

The simplest use case. Specify a unique name substring which matches the container you want.

Specify by name, two results

$ dcs -au
The following list matches your string: '-au'
ID             IMAGE NAME                         CONTAINER NAME                     
ee6daaa57386   qmcgaw/gluetun                     vpn-gluetun-au                     
d9191e86f544   qmcgaw/gluetun                     vpn-gluetun-au-searx               
The string you used is not unique, try being more specific

Selecting ee6daaa57386 qmcgaw/gluetun vpn-gluetun-au  [Y/n]: n

Connecting to vpn-gluetun-au-searx
(control-d to exit)
vpn-gluetun-au-searx-579974847c-4gdxb s root [/]$ 

In this case the -au is the search term. It is not an option.

When there are only two results, you can choose which you want. The script captures the full ID and recurses back into itself. This example demonstrates the correct handling of a substring starting with a hyphen.

Specify by name, multiple results

$ dcs glue
The following list matches your string: 'glue'
ID             IMAGE NAME                         CONTAINER NAME                     
67c50f1fbd73   qmcgaw/gluetun                     vpn-gluetun-usa                    
ec444ac4af84   qmcgaw/gluetun                     vpn-gluetun-overseas               
ee6daaa57386   qmcgaw/gluetun                     vpn-gluetun-au                     
d9191e86f544   qmcgaw/gluetun                     vpn-gluetun-au-searx               
The string you used is not unique, try being more specific
$ dcs ee6

Connecting to vpn-gluetun-au
(control-d to exit)
vpn-gluetun-au-5c76f8bd84-qbgzh s root [/]$ 

When multiple results are returned for matching containers, a list of matching containers is shown, allowing the user to use a more specific string, either ID or name, to enter the CLI.

Conclusion

In this post, we’ve explored a shell script designed to streamline the process of accessing the command line interface of a running Docker container. This tool is particularly beneficial when working with Kubernetes without kubectl, allowing you to quickly and efficiently interact with your containers. We’ve delved into the script’s functionality, dissected its code, and provided practical examples of its usage.

Whether checking the contents of a mounted volume in WordPress or managing multiple container setups, this script can significantly enhance your productivity by reducing the guesswork and time spent on routine tasks. Remember, you can customize the script to suit your needs and working environment.

We hope you find this script useful in your Docker and Kubernetes operations. As always, we encourage you to experiment with the script, adapt it to your needs, and share your experiences and feedback. Happy Dockering!