File: runeach.sh
   1 #!/bin/sh
   2 
   3 # The MIT License (MIT)
   4 #
   5 # Copyright (c) 2026 pacman64
   6 #
   7 # Permission is hereby granted, free of charge, to any person obtaining a copy
   8 # of this software and associated documentation files (the "Software"), to deal
   9 # in the Software without restriction, including without limitation the rights
  10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 # copies of the Software, and to permit persons to whom the Software is
  12 # furnished to do so, subject to the following conditions:
  13 #
  14 # The above copyright notice and this permission notice shall be included in
  15 # all copies or substantial portions of the Software.
  16 #
  17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23 # SOFTWARE.
  24 
  25 
  26 # runeach [option...] [command...] [args...]
  27 #
  28 #
  29 # RUN EACH shows each command about to run on stderr, then runs each command
  30 # using stdin lines to add a final argument to the arguments given explicitly
  31 #
  32 # The options are, available both in single and double-dash versions
  33 #
  34 #   -d, -dry, -dryrun, -dry-run
  35 #       run nothing, and show would-be completed commands to stdout
  36 #
  37 #   -h, -help
  38 #       show this help message
  39 #
  40 #   -m, -merge
  41 #       merge outputs, showing both banners and results on stdout
  42 #
  43 #   -q, -quiet, -s, -silent
  44 #       just run the commands, without showing them on stderr
  45 
  46 
  47 quiet=0
  48 dry_run=0
  49 merged=0
  50 
  51 while [ $# -gt 0 ]; do
  52     case "$1" in
  53         -d|--d|-dry|--dry|-dryrun|--dryrun|-dry-run|--dry-run)
  54             dry_run=1; shift; continue
  55         ;;
  56 
  57         -h|--h|-help|--help)
  58             awk '/^# +runeach /, /^$/ { gsub(/^# ?/, ""); print }' "$0"
  59             exit 0
  60         ;;
  61 
  62         -m|--m|-merge|--merge)
  63             merged=1; shift; continue
  64         ;;
  65 
  66         -q|--q|-quiet|--quiet|-s|--s|-silent|--silent)
  67             quiet=1; shift; continue
  68         ;;
  69 
  70         -) break ;;
  71 
  72         --) shift; break ;;
  73 
  74         -*)
  75             printf "unsupported option '%s'\n" "$1" >&2
  76             exit 1
  77         ;;
  78     esac
  79 
  80     break
  81 done
  82 
  83 IFS="$(printf "\n")"
  84 
  85 # with no command/args given, just run lines from the stdin
  86 if [ $# -eq 0 ]; then
  87     while read -r line; do
  88         # ignore empty lines
  89         if echo "${line}" | grep -E -q '^ *\r?$'; then
  90             continue
  91         fi
  92 
  93         # ignore commented lines
  94         if echo "${line}" | grep -E -q '^ *#'; then
  95             continue
  96         fi
  97 
  98         if [ "${dry_run}" -eq 1 ]; then
  99             printf "%s\n" "${line}"
 100             continue
 101         fi
 102 
 103         if [ "${quiet}" -eq 0 ]; then
 104             if [ "${merged}" -ne 0 ]; then
 105                 printf "\033[7m%-80s\033[0m\n" "${line}"
 106             else
 107                 printf "\033[7m%-80s\033[0m\n" "${line}" >&2
 108             fi
 109         fi
 110 
 111         sh -c ${line}
 112     done
 113 
 114     exit 0
 115 fi
 116 
 117 cmd="$1"
 118 shift
 119 
 120 if [ $# -eq 0 ]; then
 121     pre="${cmd}"
 122 else
 123     pre="${cmd} $*"
 124 fi
 125 
 126 while read -r line; do
 127     # ignore empty lines
 128     if echo "${line}" | grep -E -q '^ *\r?$'; then
 129         continue
 130     fi
 131 
 132     # ignore commented lines
 133     if echo "${line}" | grep -E -q '^ *#'; then
 134         continue
 135     fi
 136 
 137     if [ "${dry_run}" -eq 1 ]; then
 138         printf "%s %s\n" "${pre}" "${line}"
 139         continue
 140     fi
 141 
 142     if [ "${quiet}" -eq 0 ]; then
 143         if [ "${merged}" -ne 0 ]; then
 144             printf "\033[7m%-80s\033[0m\n" "${pre} ${line}"
 145         else
 146             printf "\033[7m%-80s\033[0m\n" "${pre} ${line}" >&2
 147         fi
 148     fi
 149 
 150     "${cmd}" "$@" ${line}
 151 done