Удалить все файлы, кроме одного, соответствующего заданному шаблону подстановки в каталоге

Да, расширение имени файла при подстановке команд и другие расширения, как правило, нежелательны¹ и не выполняются по умолчанию в zsh, за исключением эмуляции sh/ksh (globsubst, опция ).

Хотя вы можете использовать ${~$(...)}, чтобы запросить использование globsusbt для этой конкретной подстановки команд (и, кстати, вам не нужна часть paste, пробел и новая строка находятся в значении по умолчанию $IFS), гораздо лучше сделать это с помощью zsh:

ids=($(qstat))
tail -F logs/*${^ids}

Обратите внимание: если какой-либо из этих шаблонов не соответствует какому-либо файлу, выполнение команды будет прервано.

tail -F logs/*${^ids}(N)

(где (N)активирует nullglob для этих глобусов )избегал бы этого, но запускал бы tail -Fбез аргументов, если бы файла вообще не было (см. Почему nullglob не По умолчанию?).

Ты тоже можешь:

logs=(logs/*${^$(qstat)}(N))
(($#logs)) && tail -F $logs

¹ Тот факт, что bashи другие Bourne -подобные оболочки делают это, можно рассматривать как ошибку. Это одна из причин, по которой вам нужно заключать в кавычки все ваши переменные или почему вам нужно set -o noglobперед использованием $(...)без кавычек, когда вам нужна только разделяющая часть этого split+glob. Все более современные оболочки, которые не несут багаж снарядов Борна, таких как rc, esили fish, также не несут этого.

4
17.06.2021, 16:37
5 ответов
#!/bin/bash

declare -A seen

for name in *.__*.pdf; do
        prefix=${name%%.__*.pdf}

        if [[ -z ${seen[$prefix]} ]]; then
                printf 'keeping "%s"\n' "$name"
                seen[$prefix]=1
        else
                printf 'deleting "%s"\n' "$name"
                # rm -f -- "$name"
        fi
done

Приведенный выше сценарий извлекает префикс из каждого имени файла, соответствующего шаблону подстановки имени файла *.__*.pdfв текущем каталоге. Если префикс ранее не встречался, файл сохраняется. В противном случае файл удаляется (команда rmв настоящее время закомментирована для безопасности ).

Чтобы отслеживать, какие префиксы были просмотрены, они сохраняются как ключи в ассоциативном массиве с именем seen. Ассоциативные массивы были представлены в bashвыпуске 4.


Поскольку любой файл, соответствующий *.__*.pdfс тем же префиксом, является «эквивалентным», простое переименование всех этих файлов с одним и тем же именем сведет их к одному файлу.

Это не требует ассоциативного массива и может быть легко выполнено с помощью/bin/sh:

#!/bin/sh

for name in *.__*.pdf; do
        prefix=${name%%.__*.pdf}

        printf 'moving "%s" to "%s.__.pdf"\n' "$name" "$prefix"
        # mv -f -- "$name" "$prefix.__.pdf"
done

Здесь все файлы с префиксом fooперемещены в имяfoo.__.pdf(команда mvзакомментирована для безопасности ).

7
28.07.2021, 11:24

Вы можете использовать комбинацию findи awk:

.
 find./ -mindepth 1 -maxdepth 1 -type f -print0 |
 awk 'BEGIN {RS="\x00" ; FS="."}
      seen[$2]++ {system("rm '\''"$0"'\''")}'

Это использует NUL в качестве разделителя файлов, поэтому также может обрабатывать такие вещи, как новые строки в именах. Предполагается, что .используется в качестве разделителя префиксов! Если вам нужен первый (по словарному порядку )каждой партии, вы также можете передать результаты findчерезsort:

 find./ -mindepth 1 -maxdepth 1 -type f -print0 | sort -z |
 awk....

Для точного соответствия шаблону .__вы также можете использовать:

awk 'BEGIN {RS="\x00" ; FS=".__" } seen++[$1] {... }'
0
28.07.2021, 11:24

Сzsh:

all=(*.__*.pdf)
typeset -A hash
for f ($all) hash[${f%%.__*}]=$f
keep=($hash)
rm -f -- ${all:|keep}

Среди слов с одинаковым префиксом будет сохранено последнее в лексическом порядке. Вы можете изменить этот порядок с помощью all=(*.__*.pdf(On))вместо all=(*.__*.pdf).

6
28.07.2021, 11:24

Сzsh:

declare -A h=()
rm -- *(e:'((h[${REPLY%%.*}]++))':)

К сожалению, это не позволяет установить порядок. Добавление квалификаторов glob o/ Oздесь только изменит порядок после , исключая первый из каждого, а eоценивает, следует ли включать/исключать файл в том порядке, в котором файлы найдены (т.е. неупорядоченные, так как *(oN)вернет их ).

2
28.07.2021, 11:24

Используя perl, мы можем открыть дескриптор каталога в текущем каталоге и прочитать все простые файлы, имена которых соответствуют указанному критерию. Имена первых из этих файлов с таким же префиксом выводятся на стандартный вывод, а последующие файлы с этим префиксом удаляются. Префиксы представляют собой ключи хэша%h

perl -le '
  opendir my $dh, "."
    or die qq(Cannot opendir ".": $!);

  -f && /^(.+?)\.__.*\.pdf$/ and
    !$h{$1}++ ? print() : unlink()
  while readdir $dh;

  closedir $dh;
'

Использование команды поиска:

fx() {
  set -- "$1" "$tmpdir/${1%%.__*.pdf}"
  if [ ! -s "$2" ]; then
    echo x > "$2"
    shift "$#"
  else
    rm -f "$1"
  fi
  return "$#"
}
export -f fx
tmpdir="$(mktemp -d)" \
find. ! -name. -prune -type f \
-name '?*.__*.pdf' \
-exec sh -c 'fx "$1"' find-sh {} \; -print;

Использование python3 вместе с модулем pathlib с той же идеей, что и выше:

python3 -c 'from pathlib import Path
seen = {}
for f in Path(".").glob("?*.__*.pdf"):
  if f.is_file():
    p = f.name.find(".__")
    k = f.name[0:p]
    if p > 0:
      if k in seen: f.unlink()
      else:
        print(f.name)
        seen[k]=1
'
1
28.07.2021, 11:24

Теги

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