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

/path/to/job.py &
pid=$!
sleep 30
if kill -0 $pid; then kill -TERM $pid; fi
0
04.05.2020, 08:59
1 ответ

Использование bash(, которое на самом деле не является инструментом POSIX, но поскольку вы упомянули об этом явно):

#!/bin/bash

names=( *:* )

printf '%s\n' "${names[@]##*:}" | sort | uniq -c |
while read count hash; do
    if [[ $count -gt 1 ]]; then
        echo 'Would delete/move these:'
        printf '%s\n' *:"$hash"
    fi
done

Это собирает все имена в текущем каталоге, содержащие символ :, в массив names. Предполагается, что шаблон *:*соответствует только интересующим нас файлам и что никакие другие файлы не имеют таких имен.

Расширение "${names[@]##*:}"приведет к списку только хэшей, которые мы сортируем и подсчитываем с помощью sort | uniq -c.

Результат этого считывается в countи hashв цикле while read, и если счетчик больше единицы, мы знаем, что хэш дублируется. Если хэш повторяется, шаблон *:"$hash"будет соответствовать всем именам, имеющим этот хэш.

Если вы хотите удалить все файлы с повторяющимися хэшами, вы можете

rm -f./*:"$hash"

Если вы хотите сохранить один из файлов, то вместо этого сделайте, например

dupnames=(./*:"$hash" )
rm -f "${dupnames[@]:1}"

Это устанавливает массив dupnamesв совпадающие имена и удаляет все, кроме первого, из файловой системы.

Возможно, вы захотите запустить с некоторыми включенными выводами отладки и сначала сrmотключенным , пока не убедитесь, что это действительно работает:

#!/bin/bash

names=( *:* )

printf '%s\n' "${names[@]##*:}" | sort | uniq -c |
while read count hash; do
    if [[ $count -gt 1 ]]; then
        echo 'Would delete/move these:'
        dupnames=(./*:"$hash" )
        echo rm -f "${dupnames[@]:1}"
    fi
done

Вариант POSIX shвышеприведенного:

#!/bin/sh

for name in *:*; do
    printf '%s\n' "${name##*:}"
done | sort | uniq -c |
while read count hash; do
    if [ "$count" -gt 1 ]; then
        echo 'Would delete/move these:'
        set --./*:"$hash"
        shift
        echo rm -f "$@"
    fi
done

Вариация последнего варианта, в котором sort | uniq -cубирается с помощьюawk:

#!/bin/sh

for name in *:*; do
    printf '%s\n' "${name##*:}"
done |
awk '    { count[$0]++ }
     END { for (hash in count) if (count[hash] > 1) print hash }' |
while read hash; do
    echo 'Would delete/move these:'
    set --./*:"$hash"
    shift
    echo rm -f "$@"
done

Фрагмент awkтакже может заменить sort | uniq -cв других фрагментах кода в этом ответе, но обратите внимание, что в финальном цикле теперь не нужно проверять, больше ли число единиц, и что он только считывает хеши.

1
28.04.2021, 23:16

Теги

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