This answer focuses on zsh. Most of it can't be done easily in bash.
Many common cases can be done with wildcards. In particular:
- Everything with a fixed prefix:
foo-*
- Everything with a fixed prefix followed by one more letter within a range:
foo[b-m]* (includes foobar, food, foomz1, but not fooar or foon)
- Numbers in a range:
IMG-<7-42>.* (includes IMG-8.PNG and IMG-0042.JPG but not IMG-77.JPG)
With glob qualifiers, there's an easy way to identify a range, but it requires counting: foo*([1,3]) matches the first 3 files listed by foo*, whatever they are (or all files if there are fewer than 3). This takes place after whatever sorting is done, for example foo*(om[1,3]) matches the three most recently modified files whose name starts with foo.
You can make zsh figure out the numbers for you. Do it in two steps: first put all the matches in an array, then find the endpoints using the subscript flags i and I (also e if you want to prevent any wildcard matching): $a[$a[(i)foo],$a[(I)bar]] is the part of the array $a from the element foo to the element bar inclusive, and empty if either foo or bar is not present.
a=(*.txt(oL))
# List the files that appear between small.txt and large.txt in a listing by size.
# Note that files that have the same size as one of the bounds may or may not be included.
echo $a[$a[(I)small.txt],$a[(I)large.txt]]
So here's a function that implements exactly the requirements of the question (except the exact syntax, which can't be done):
# Usage: select_range FROM TO WILDCARD_PATTERN
# Sets the array $s to the files matching PATTERN from FROM to TO inclusive.
function select_range {
if (($# < 2)); then
echo >&2 "select_range: missing range arguments"
return 120
fi
local from=$1 to=$2
shift 2
from=$@[(ie)$from]
if ((from == 0)); then
echo >&2 "select_range: not matched: $from"
fi
to=$@[(Ie)$to]
if ((to == 0)); then
echo >&2 "select_range: not matched: $from"
fi
s=($@[$from,$to])
}
Usage: select_range aoeitoae.txt oaie.txt * && rm $s
The e glob qualifier lets you write arbitrary code to filter results, but it already starts getting a little unwieldy. Quoting can be tricky in complex cases; to keep things simple, use ' as the delimiter (which needs to be quoted with a backslash) and put the filter code in single quotes, meaning the pattern looks like this: foo-*(e\''code goes here'\'). (If the quoting gets too complicated, write a function and use the + qualifier.) To filter files that come after aoeitoae.txt and before oaie.txt in lexicographic order: *(e\''! [[ $REPLY < aoeitoae.txt || $REPLY > oaie.txt ]]'\').
Note that the comparisons done in the filter don't necessarily use the same order as the wildcard expansion. For example, foo-*(n) lists foo-9 before foo-10 thanks to the n qualifier, but [[ foo-9 > foo-10 ]] in a string comparison, and there's no conditional operator similar to > that compares integer substrings numerically. If you want to do a string comparison with integer parts sorted numerically, you can use the n parameter expansion flag for array sorting and check that it keeps the matched name in the middle: *(ne\''a=(b11r $REPLY f10o); [[ $a[2] == "${${(@n)a}[2]}" ]]'\')) includes b101r, b11s, d1, f02o, …, but not b9r, f011, …
If you're matching files by date, you can use the -nt conditional (note that a file is not newer than itself): *(ome\''! [[ $REPLY -ot from || $REPLY -nt to ]]'\') only includes files modified between the modification time of from and the modification time of to, inclusive.
rangerormcmight make sense :) – Marcus Müller Feb 15 '23 at 20:20ls -1 | sed -n -e '/first_file/,/last_file/p'? – U. Windl Feb 16 '23 at 00:37sedwon't work, but I'll propose a nice one usingedinstead. – U. Windl Feb 17 '23 at 09:32rm [a-o]*.txtwould remove the 3 files you want. Perhaps you could craft a better example, like one where there's anoab....txtyou don't want to remove, and you don't want to userm -ito manually say yes to all but the last; easy to get that wrong. – Peter Cordes Feb 18 '23 at 01:35