|
#!/bin/sh |
|
# A simple script to locate files associated with a software install |
|
# (Should work on Linux, BSD and macOS [13+]) |
|
# |
|
# This finds files whose metadata changed (which, for installed files, |
|
# typically corresponds to their creation) around the same time as a |
|
# reference file or an executable found in $PATH, within common install |
|
# locations. |
|
# |
|
# It works by noting the UNIX Change time (ctime) of the reference file and |
|
# looking for files altered within a small time window around that |
|
# timestamp. |
|
# |
|
# Note: Modify time (mtime) would be far less reliable, as the original |
|
# modification time is often retained on files during installation. |
|
# |
|
# Usage: |
|
# |
|
# $ ./related.sh <file-or-command> [seconds] |
|
# |
|
# If a second argument is provided, the script will interpret it as seconds |
|
# before and after the reference file's Change time (otherwise 10 seconds is |
|
# assumed). Increase the value if you think some files may have been missed. |
|
# Decrease it if you feel that too many files were found. |
|
# |
|
# Note: There are always possibilities for false positives, so review the |
|
# files before removal. |
|
# |
|
# Uses the concepts explained in greater detail here: |
|
# <https://gist.github.com/ruario/a36052a1ae1de4edbc6ad39fe39e5385> |
|
|
|
set -eu |
|
|
|
# Require at least one argument, the file or command (resolve reference |
|
# file) |
|
[ -n "${1:-}" ] || { echo "Usage: $0 <file-or-command> [seconds]" >&2; exit 1; } |
|
case "$1" in |
|
*/*) [ -e "$1" ] && reference="$1" ;; |
|
*) reference="$(command -v "$1" || :)" ;; |
|
esac |
|
[ -n "${reference:-}" ] || { echo "Error: \"$1\" is not a file or command" >&2; exit 1 ; } |
|
|
|
# Capture the ctime UNIX timestamp of the reference file and define a date |
|
# command, handling the differences between the GNU and BSD tools |
|
OS="$(uname -s)" |
|
case "$OS" in |
|
Darwin|*BSD*) ctime="$(stat -f %c "$reference")"; date_cmd='date -r ' ;; |
|
Linux) ctime="$(stat -c %Z "$reference")"; date_cmd='date -d@' ;; |
|
*) echo "$OS is not supported" >&2; exit 1 ;; |
|
esac |
|
|
|
# Define the time window in a format that the touch command can use |
|
window="${2:-10}" |
|
case "$window" in |
|
*[!0-9]*) echo "Usage: $0 <file-or-command> [seconds]" >&2; exit 1 ;; |
|
esac |
|
start_time="$($date_cmd$((ctime-window)) '+%Y%m%d%H%M.%S')" |
|
end_time="$($date_cmd$((ctime+window)) '+%Y%m%d%H%M.%S')" |
|
|
|
# Create two temp files (to be used to record the time window) |
|
start_file="$(mktemp)" |
|
end_file="$(mktemp)" |
|
trap 'rm "$start_file" "$end_file"' EXIT |
|
|
|
# Set the mtime of the temp files to ctime range (time window) |
|
touch -t "$start_time" "$start_file" |
|
touch -t "$end_time" "$end_file" |
|
|
|
# Find files in common install directories whose metadata changed (ctime) |
|
# within the time window of the reference (by reading the mtime values of |
|
# the temp files) |
|
find "$HOME/.local" "$HOME/bin" /etc /opt /usr -newercm "$start_file" \! -newercm "$end_file" \! -type d 2>/dev/null ||: |
|
|
|
# Some installers drop files directly in $HOME, so check the top level as |
|
# well |
|
find "$HOME" -maxdepth 1 -newercm "$start_file" \! -newercm "$end_file" \! -type d 2>/dev/null ||: |