#!/bin/sh set -eu usage() { cat <<-'EOF' Usage: uninclude -E REGEXP -- COMMAND... uninclude -h EOF } help() { cat <<-'EOF' Options: -E REGEXP the test expression for lines to be deleted -h, --help show this message COMMAND the build/test command used to validate the uninclusion Read list of files from STDIN, and for each try to delete all lines that match REGEXP and see if COMMAND passes. Do this for every match untill all lines that could be removed are removed. Examples: Remove all non-required "#include" lines from lib.c and main.c: $ find lib.c main.c | uninclude -E '^#include ' -- cc $CFLAGS lib.c: 3 lines unincluded main.c: nothing to uninclude Remove all non-required "imports" from all files that ends with ".py": $ git ls-files | grep '\.py$' | uninclude '^import ' -- pytest EOF } for flag in "$@"; do case "$flag" in (--) break ;; (--help) usage help exit ;; (*) ;; esac done while getopts 'E:h' flag; do case "$flag" in E) REGEXP="$OPTARG" ;; (h) usage help exit ;; (*) usage >&2 exit 2 ;; esac done shift $((OPTIND - 1)) eval "$(assert-arg -- "${REGEXP:-}" '-E REGEXP')" ORIG="$(mkstemp)" VALID="$(mkstemp)" MATCHES="$(mkstemp)" LINES="$(mkstemp)" trap 'rm -f "$ORIG" "$VALID" "$MATCHES" "$LINES"' EXIT while read -r file; do cp "$file" "$ORIG" cp "$file" "$VALID" NTH=1 while true; do if ! grep -nE "$REGEXP" "$file" > "$MATCHES" || [ "$NTH" -ge "$(wc -l < "$MATCHES")" ]; then FROM="$(wc -l < "$ORIG")" TO="$( wc -l < "$file")" if [ "$FROM" -eq "$TO" ]; then printf '%s: nothing to uninclude\n' "$file" >&2 else printf '%s: %s lines unincluded\n' "$file" $((FROM - TO)) >&2 fi break fi line="$(cut -d: -f1 "$MATCHES" | lines "$NTH")" sed "${line}d" "$file" | sponge "$file" if ! "$@" "$file"; then cp "$VALID" "$file" NTH=$((NTH + 1)) else cp "$file" "$VALID" fi done done