I have a large collection of files that look like this:

Some Name da-1234567-1.py
Some Name da-1234567-2.py
Some Name da-1234567-4.py
Other Name di-5678912-3.py
Other Name di-5678912-4.py
Other Name di-5678912-5.py

I would like to remove all versions and only keep:

Some Name da-1234567-4.py
Other Name di-5678912-5.py

I realise my question is similar to Find latest folder/file version and remove - no time stamp data . However its answer does not work for me and my knowledge of bash and/or regular expression is insufficient to change the solution to cater my needs. Unfortunately I do not have the reputation score to comment thus I do not see another option than to post a new question.

I tried the following from above question:

for file in *.*
  [[ -d "$file" || $file =~ _[[:digit:]]{3}\. ]] && continue
  echo -n "Considering $file: " >&2


  # Look at matching files
  for version in "${file%.$extn}"_???."$extn"
      [[ -f "$version" ]] || continue

      # Save every one. Identify the current last
      echo -n "$version " >&2
  echo "==> keep $keep" >&2

  # Delete them all except the last
  for version in "${versions[@]}"
      [[ "$version" != "$keep" ]] && echo rm -f -- "$version"
  [[ "$keep" != "$file" ]] && echo mv -f -- "$keep" "$file"

I realise that it probably goes wrong in this part: $file =~ _[[:digit:]]{3}\., since opposed to the above question my file ending is -n and not _nnn, but I do not see how to fix it.

  • Are all your files ending in -, a single digit, .py? If so just change the _ to a -, the 3 to a 1 and you should be good to go. You could also delete the {3} part because 1 is default.
    – jw013
    Jun 4, 2018 at 21:27
  • Yes they are all the same format. That was my first guess before asking this question, without success unfortunately
    – YStimmer
    Jun 5, 2018 at 8:02

2 Answers 2


Software tools one-liner, using ls -v and sort -V which sort by version number:

{ ls -Qrv *.py |rev | uniq -f 1 | rev; ls -Q *.py; } | sort -V | uniq -u | xargs rm

Using uniq -f 1 depends on the file naming format being consistent -- the surrounding revs are needed because -f has no other way of ignoring the last field.

  • Thanks so much, that solved it! There is a small typo though: there should be a space behind the {
    – YStimmer
    Jun 5, 2018 at 8:09
  • 1
    Note that while ls -Q (a GNU extension like rev, uniq -f, sort -V) would work with the OP's sample, it is generally not compatible with the format expected by xargs. It wouldn't work properly with file names that contain double quotes, backslashes or control characters for instance. Jun 5, 2018 at 9:21

With zsh:

# all *.<number>.py files in "n"umerical order

# associative array whose key is the part before the last "-"
typeset -A latest
for f ($files) latest[${f%-*}]=$f

# plain array with the values of the associative array

# array subtraction:
echo rm -- ${files:|keep}

(remove echo if happy).

That makes no assumption on what characters the rest of the file names may contain. With bash (or zsh or ksh) and GNU tools:

xargs -r0a <(printf '%s\0' *-*.py |
  grep -zEe '-[[:digit:]]+\.py$' |
  sort -zrV |
  awk -vRS='\0' -vORS='\0' '
    {key = $0; sub(/-[^-]*$/, "", key)}
    seen[key]++') echo rm --

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .