Как выбрать диапазон файлов по части имени

Как выбрать диапазон файлов по части имени

Я хочу выбрать диапазон файлов, используя часть их имени, для использования при копировании, перемещении и т. д.

Случайный пример списка:

  1. альфа1а
  2. альфа2аа
  3. альфа3абв
  4. бравоА1
  5. бравоB2bb
  6. Чарли
  7. дельтаА1фдфд
  8. дельтаА2дафа
  9. дельтаА2222
  10. дельтаС1
  11. ... (куча других файлов)

Я хотел бы запустить какую-то команду, которая идет от alpha2* до deltaA*, которая выберет 2-9 в списке. Таким образом я могу выбрать диапазон на основе имени, не беспокоиться о том, сколько файлов на самом деле, и не получить ничего лишнего.

решение1

start=alpha2*
end=deltaA*

for file in *
do
    if [[ $file > $start && ( $file < $end || $file == $end ) ]]; then
        echo $file
    fi
done

Вместо echo вы можете сохранить файлы в массиве:

array+=($file)

А затем используйте массив для копирования, перемещения файлов... или просто выполните команду внутри цикла for.

решение2

Shell-программирование не очень хорошо справляется с лексикографическими сравнениями. В bash, ksh или zsh вы можете использовать< условный оператордля сравнения двух строк (POSIX sh поддерживает только числовые сравнения).

run_in_range () {
  start_at="$1"; end_before="$2"; command="$3"; shift 3
  for item do
    if [[ ! ($x < $start_at) && ($x < $end_before) ]]; then
      "$command" "$item"
    fi
  done
}
run_in_range alpha2 deltaB some_command *

Обратите внимание, что я использовал первый исключенный элемент в качестве второго аргумента: deltaBэто наименьшая строка, которая идет после всех строк, начинающихся с deltaA. Если вы хотите передать deltaAв качестве аргумента, вы можете сделать это следующим образом.

run_in_inclusive_range () {
  start_at="$1"; end_on_prefix="$2"; command="$3"; shift 3
  for item do
    if [[ ! ($x < $start_at) && ($x < $end_on_prefix || $x == "$end_on_prefix"*) ]]; then
      "$command" "$item"
    fi
  done
}
run_in_inclusive_range alpha2 deltaA some_command *

Этот код не зависит от порядка подстановочного знака: он работает, даже если список элементов не отсортирован. Это делает код более надежным, но немного медленнее, поскольку он должен пройти по всему списку.

Вот альтернативный подход, который опирается только на возможности POSIX. Он использует sortдля сравнения строк. Этот предполагает, что в элементах нет новых строк. Эта функция восстанавливает настройки по умолчанию для IFSи подстановки (возможно, но раздражающе сложно восстановить настройки окружения).

run_in_range () {
  IFS='
'; set -f
  start_at="$1"; end_before="$2"; command="$3"; shift 3
  set $({
          printf '%s1\n' "$@"
          printf '%s0\n' "$start_at" "$end_before"
        } | sort | sed '/0$/,/0$/ s/1$//p')
  unset IFS; set +f
  for item do
    "$command" "$item"
  done
}

Объяснение:

  1. Создайте список, содержащий элементы с 1добавленными элементами и границы с 0добавленными элементами.
  2. Сортируйте список.
  3. Выведите строки между двумя граничными линиями (строки, заканчивающиеся на 0), но только строки, заканчивающиеся на 1(т. е. элементы), и без завершающего 1.

решение3

Мне кажется, что решить эту проблему проще, чем кажется:

Предположим, что есть способ получить список, по одному файлу на строку.
Для текущего каталога:

list="$(printf '%s\n' *)"

Просто добавьте ограничения в список и отсортируйте его:

nl='
'
lim1="alpha2"
lim2="deltaA"

echo -- "$list""$nl""$lim1""$nl""$lim2" | sort

Затем начните печать с lim1 (следя за тем, чтобы lim1 и lim2 не совпадали с существующими именами файлов) и остановитесь на lim2.

Также, чтобы упростить задачу, выберите самую короткую строку, которая находится непосредственно перед файлом, который вы хотите распечатать, а также самую короткую строку, которая находится непосредственно после последнего файла, который необходимо выбрать.

Полезным является эксклюзивный список (тот, который не включает ограничения),
но предусмотрены оба решения.

Основные ценности:

list="$(printf '%s\n' *)"

lim1=alpha2
lim2=deltaA

Решение с помощью awk для инклюзивного списка:

echo "awk----------------inclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1,$0==lim2{print}'

Решение с помощью awk для эксклюзивного списка:

echo "awk----------------exclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1{p=1;next};$0==lim2{p=0};p'

Решение с оболочкой для инклюзивного списка:

echo "shell---------------inclusive"
show=0
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    while IFS="$nl" read -r line; do
        [ "$line" = "$lim1" ] && show=1
        [ "$show" = "1"     ] && printf '%s\n' "$line"
        [ "$line" = "$lim2" ] && show=0
    done

И эксклюзивное решение для оболочки:

show=0
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    while read -r line; do
        [ "$line" = "$lim2" ] && show=0
        [ "$show" = "1"     ] && printf '%s\n' "$line"
        [ "$line" = "$lim1" ] && show=1
    done

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