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

Запуск PHP-скриптов от имени root - опасная практика. Веб-серверы делают все возможное, чтобы предотвратить повышение привилегий, и применяют различные меры, такие как chroot jail. Я считаю, что это может быть причиной ваших проблем.

В частности, dpkg нуждается в доступе к файлам в /var/lib/dpkg/, чтобы нормально функционировать. Вы можете проверить, есть ли у вас доступ к этому каталогу из ваших PHP скриптов, например, попытавшись перечислить файлы в нем, или прочитать из /var/lib/dpkg/status. Если вы этого не делаете (даже при запуске от имени root), значит, вы действительно находитесь в тюрьме. Конечно, вы можете просто отключить jail (как это сделать, зависит от веб-сервера), но я бы не рекомендовал этого делать. Есть причина, по которой это было сделано, и обход механизмов безопасности подвергает вас всевозможным атакам.

4
12.02.2016, 23:50
6 ответов

Если файлы находятся в текущем рабочем каталоге и их имена соответствуют образцам (одиночный дефис перед числом), следующий 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 не требует такой стабильности, но, согласно их руководствам, реализации BSD и GNU {Free, Net, Open} обеспечивают ее. Если вам нравится искушающая судьба:

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

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

1
27.01.2020, 20:50

Я бы разделил файл на части с разделителями-таблицами для более надежного и настраиваемого разбора имен файлов, затем использовал 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 и не имеют 0-заполнения. Этот метод сортирует файлы лексикографически, затем при разборе списка первая часть меняется, используется самое последнее встреченное имя файла. Когда имена файлов выглядят следующим образом, это не сработает:

file-9.txt
file-10.txt

потому что файл-10.txt появится раньше файла-9. Приведенный выше сценарий awk выполняет численное сравнение.

ВНИМАНИЕ: Имена файлов с табуляцией и новыми строками приведут к тому, что эта программа заглохнет.

CAVEAT 2: Если возможно несколько расширений на префикс имени файла, нам придется внести некоторые изменения, чтобы сделать это правильно.

0
27.01.2020, 20:50

Если pwd является исходным dir

cp -t /path_to_destination $(
      ls -v *[0-9].txt |
      sed '$b;N;/^\(.*\)[0-9]\+\.txt\n\1[0-9]\+\.txt/!P;D')

ПРИМЕЧАНИЕ: Если в именах файлов есть пробелы, то для правильной работы их следует предварительно экранировать. + см. другие ограничения в комментариях Stéphane Chazelas

1
27.01.2020, 20:50

Это не ответ командной строки, как таковой, но если предположить, что у вас есть bash версии >= 4, то вот сценарий bash, который собирает все файлы *.txt, определяет их нумерованный суффикс, затем сохраняет самый высокий из увиденных суффиксов в ассоциативный массив (индексированный по базовой части имени файла перед нумерованным суффиксом). Затем выводит образец команды cp для каждого имени файла с самым высоким суффиксом.

Отрегулируйте назначение "somedir" по мере необходимости.

#!/bin/bash

declare -A highest
for f in *.txt
do
  base=${f%%.txt}
  prefix=${base%-*}
  postfix=${base/$prefix-/}
  if [[ ${highest[$prefix]} -lt $postfix ]]
  then
    highest[$prefix]=$postfix
  fi
done

for prefix in "${!highest[@]}"
do
  escaped=${prefix//\"/\\\"}
  printf "cp -- \"%s\" somedir/\n" "$escaped-${highest[$prefix]}.txt"
done

В каталоге с этими файлами:

-dash-1.txt
-dash-2.txt
double"quote-3.txt
file'here-1.txt
filetwo'here'-2.txt
name_file-1.txt
name_file-2.txt
name_file-3.txt
somefile-5.txt
somefile-60.txt
some_other_file-1.txt
some_other_file-2.txt
space file-1.txt
space file-2.txt

Вывод (отсортированный вручную, для более удобного чтения):

cp -- "-dash-2.txt" somedir/
cp -- "double\"quote-3.txt" somedir/
cp -- "file'here-1.txt" somedir/
cp -- "filetwo'here'-2.txt" somedir/
cp -- "name_file-3.txt" somedir/
cp -- "somefile-60.txt" somedir/
cp -- "some_other_file-2.txt" somedir/
cp -- "space file-2.txt" somedir/
0
27.01.2020, 20:50
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
1
27.01.2020, 20:50

С zsh :

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

Теги

Похожие вопросы