Сортировать файлы по наибольшему номеру в имени файла

Сортировать файлы по наибольшему номеру в имени файла

У меня есть куча файлов, все они названы примерно так:

name_file-1.txt
name_file-2.txt
name_file-3.txt
some_other_file-1.txt
some_other_file-2.txt

Существуют тысячи различных имен файлов, некоторые из которых -1.txtзаканчиваются на единицу, некоторые — на -1.txt, -2.txt...-60.txt

Мне нужно скопировать самые большие номера каждого файла, поэтому name_file-3.txt, some_other_file-2.txt. Как это сделать в командной строке Linux?

решение1

С zsh:

typeset -A greatest
for f (*-*(n)) greatest[${f%-*}]=$f
cp -- $greatest /destination
  • *-*(n): не скрытые файлы, имена которых содержат -( *-*), отсортированные по числовому значению ( (n)квалификатор glob).
  • ${f%-*}: часть имени файла до самого правого угла -(или до конца, если нет -).
  • $greatest: расширяется до непустогоценностиассоциативных массивов. Так вот, для файлов, имеющих один и тот же корень, будет расширен только файл с наибольшим номером.

решение2

files=(*)
mapfile -t prefixes < <(printf "%s\n" "${files[@]%-*}" | sort -u)
for p in "${prefixes[@]}"; do ls -v "$p"* | tail -1; done
name_file-3.txt
some_other_file-2.txt

А затем скопировать их в какой-нибудь другой каталог:

for ...; done | xargs cp -t /destination/directory

решение3

Если файлы находятся в текущем рабочем каталоге и их имена соответствуют образцам (одиночный дефис перед числом), то должен работать следующий POSIX-совместимый конвейер:

ls | sort -t- -k1,1 -k2,2rn | awk -F- 'k!=$1 {print; k=$1}' | pax -rw /path/to/dir

Компонент awk можно заменить на sort -u, если опция sort -u стабильна (так что первая строка набора всегда выбирается для представления этого набора). POSIX не требует такой стабильности, но, согласно их руководствам, реализации {Free,Net,Open}BSD и GNU ее предоставляют. Если вам нравится испытывать судьбу:

ls | sort -t- -k1,1 -k2,2rn | sort -mut- -k1,1 | pax -rw /path/to/dir

В любом случае целевой каталог не должен находиться в текущем рабочем каталоге.

решение4

Я бы разделил файл на части, разделенные табуляцией, для более надежного, настраиваемого разбора имен файлов, затем использовал бы awk, чтобы найти наивысший ранг каждой части и сообщить. Сначала попробуйте каждую часть конвейера, прежде чем переходить к следующей!

find DIR -type f <other find criteron> -print | 
perl -lne 'print join("\t",(/^(.*?-)(\d+)(\.\w+)$/))' |
awk -F\\t '$2 > f[$1] { f[$1]=$2;e[$1]=$3; } END { for (k in f) { print k f[k] e[k] }}' |
xargs cp -t <desination_directory>

Скрипт awk помещает каждое имя файла в ассоциированную запись массива, всегда сохраняя наивысший найденный ранг. Расширение сохраняется в своем собственном массиве. После обработки всех входных данных все записи массива выводятся, по одной на строку. Строка xargs cp -tкопирует все файлы в указанный вами каталог.

Есть еще один метод, которыйне будет работатьочень хорошо, если числа больше 9 и не дополнены нулями. Этот метод сортирует файлы лексикографически, затем при разборе списка первая часть меняется, используется последнее просмотренное имя файла. Когда имена файлов такие, это не сработает:

file-9.txt
file-10.txt

потому что file-10.txt появится перед file-9. Скрипт awk выше выполняет числовое сравнение.

ПРЕДОСТЕРЕЖЕНИЕ: Имена файлов с символами табуляции и новой строки приведут к сбою.

ПРЕДОСТЕРЕЖЕНИЕ 2: Если возможны несколько расширений для одного префикса имени файла, нам придется внести некоторые изменения, чтобы все было правильно.

Связанный контент