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 '18 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 '18 at 8:02

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 '18 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. – Stéphane Chazelas Jun 5 '18 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 disjunction:
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 --

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.