I often find I need to find the earliest or latest file matching a given pattern, and sometimes to choose the lowest or highest string from several possibilities.
The following four functions provide that functionality, and are robust enough to work correctly with all possible inputs. They do require the GNU implementations of sort
and head
that work with NUL-separated input.
#!/bin/sh
set -eu
first_sorted() {
${1+true} return 1
printf '%s\0' "$@" | sort -z | head -z -n1 | tr '\0' '\n'
}
last_sorted() {
${1+true} return 1
printf '%s\0' "$@" | sort -rz | head -z -n1 | tr '\0' '\n'
}
first_mtime() {
test -e "${1-}" || return 1
first=$1; shift
for i
do
test -e "$i" || return 1
test "$first" -ot "$i" || first=$i
done
printf '%s\n' "$first"
}
last_mtime() {
test -e "${1-}" || return 1
last=$1; shift
for i
do
test -e "$i" || return 1
test "$last" -nt "$i" || last=$i
done
printf '%s\n' "$last"
}
Unit tests:
# Unit tests of string ops
LC_COLLATE=C
export LC_COLLATE
nl='
'
first_sorted && exit 1
test "twenty${nl}seven" = "$(first_sorted "twenty${nl}seven")"
test "twenty${nl}five" = "$(first_sorted "twenty${nl}seven" "twenty${nl}five")"
test "" = "$(first_sorted '' "twenty${nl}five")"
last_sorted && exit 1
test "twenty${nl}seven" = "$(last_sorted "twenty${nl}seven")"
test "twenty${nl}seven" = "$(last_sorted "twenty${nl}seven" "twenty${nl}five")"
test "twenty${nl}five" = "$(last_sorted '' "twenty${nl}five")"
# Unit tests of file ops
d=$(mktemp -d)
trap 'rm -r "$d"' EXIT
cd "$d"
touch -t 12310900 "Hogmanay${nl}morning"
touch -t 12311200 "Hogmanay${nl}midday"
touch -t 12311500 "Hogmanay${nl}afternoon"
touch -t 12312200 "Hogmanay${nl}evening"
first_mtime && return 1
test "Hogmanay${nl}morning" = "$(first_mtime H*)"
last_mtime && return 1
test "Hogmanay${nl}evening" = "$(last_mtime H*)"