Перемещение файлов с узором в имени в папку с тем же узором, что и его имя

Мой каталог содержит сотни файлов и каталогов, подобных этому:

508471/
ae_lstm__ts_ 508471_detected_anomalies.pdf
ae_lstm__508471_prediction_result.pdf
mlp_508471_prediction_result.pdf
mlp__ts_508471_detected_anomalies.pdf
vanilla_lstm_508471_prediction_result.pdf
vanilla_lstm_ts_508471_detected_anomalies.pdf

598690/
ae_lstm__ts_598690_detected_anomalies.pdf
ae_lstm__598690_prediction_result.pdf
mlp_598690_prediction_result.pdf
mlp__ts_598690_detected_anomalies.pdf
vanilla_lstm_598690_prediction_result.pdf
vanilla_lstm_ts_598690_detected_anomalies.pdf

Существуют папки, имена которых имеют идентификационный номер, например 508471 и 598690.
По тому же пути, что и эти папки, находятся pdf файлы, в названии которых указан этот идентификационный номер. Мне нужно переместить все pdf файлы с одинаковым идентификатором в их имени в соответствующие каталоги.

Я пробовал следующий сценарий оболочки, но он ничего не делает. Что я делаю не так?
Я пытаюсь перебрать все каталоги, найти файлы, в имени которых есть id, и переместить их в тот же каталог:

for f in ls -d */; do
    id=${f%?}  # f value is '598690/', I'm removing the last character, `\`, to get only the id part 
    find . -maxdepth 1 -type f -iname *.pdf -exec grep $id {} \; -exec mv -i {} $f \;
done

person Birish    schedule 13.03.2020    source источник


Ответы (4)


#!/bin/sh
find . -mindepth 1 -maxdepth 1 -type d -exec sh -c '
    for d in "$@"; do
        id=${d#./}
        for file in *"$id"*.pdf; do
            [ -f "$file" ] && mv -- "$file" "$d"
        done
    done
' findshell {} +

Это находит все каталоги внутри текущего (находит, например, ./598690). Затем он удаляет ./ из относительного пути и выбирает каждый файл, который содержит результирующий идентификатор (598690), перемещая его в соответствующий каталог.

Если вы не уверены в том, что это будет делать, поместите echo между && и mv, это перечислит mv действия, которые сценарий будет выполнять.

И помните, не анализируйте ls.

person Quasímodo    schedule 13.03.2020
comment
Вы можете сделать более короткий for d; do и не забыть суффикс .pdf в вашем глобусе *"$id"*.pdf. - person Freddy; 13.03.2020
comment
@Freddy Спасибо, я отредактировал ответ. При этом нам даже не нужно [ -f "$file" ] тест перед переездом! - person Quasímodo; 13.03.2020
comment
@ Quasímodo код не загружал ни один файл :( Я получаю эту ошибку для каждого идентификатора: mv: rename *520813*.pdf to ./520813/*520813*.pdf: No such file or directory - person Birish; 13.03.2020
comment
@ Quasímodo Он пытается переименовать файлы в ./520813/*520813*.pdf? - person Birish; 13.03.2020
comment
@Birish Это из-за nullglob (я предполагаю, что у вас не было файла с идентификатором 520813 в каталоге, но был каталог 520813), о котором я забыл. Он попытался переместить несуществующий (буквальный) *520813*.pdf в соответствующий каталог. Несмотря на сообщение об ошибке, скрипту, должно быть, удалось переместить другие файлы. Проверьте правку, попробуйте, и ошибок не должно возникнуть. - person Quasímodo; 13.03.2020
comment
@ Quasímodo все еще не работает :( Однако сообщение об ошибке отсутствует. Мне нужны все файлы с именем *<ID>*.pdf, чтобы перейти в каталог с именем <ID>. Это ID может быть, например, 598690 - person Birish; 13.03.2020
comment
@ Quasímodo Я тестирую ваш скрипт, d в первом цикле, id во втором цикле верны. значение file равно *520882*.pdf. Команда mv ничего не делает. Он понимает, что *520882*.pdf - это подстановочный знак, а не точное имя файла? !! - person Birish; 13.03.2020
comment
Это сработало! Я совершил очень глупую ошибку! Не будем об этом говорить: | - person Birish; 14.03.2020

Приведенный ниже код должен выполнять требуемую работу.

for dir in */; do find . -mindepth 1 -maxdepth 1 -type f -name "*${dir%*/}*.pdf" -exec mv {} ${dir}/ \;; done

где */ будет рассматривать только каталоги, присутствующие в данном каталоге, find будет искать только файлы в данном каталоге, которые соответствуют *${dir%*/}*.pdf, т.е. имя файла, содержащее имя каталога в качестве подстроки, и, наконец, mv скопирует соответствующие файлы в каталог.

person Nitish    schedule 13.03.2020
comment
Каталоги @Birish 508471, 598690 и остальные файлы примеров находятся в одном родительском каталоге, и вам нужно переместить файлы с указанным именем каталога в качестве подстроки в этот каталог, верно? - person Nitish; 13.03.2020
comment
да, они все в одном каталоге. Мне нужно, чтобы все файлы с *<ID>*.pdf перешли в каталог с именем <ID>. Это ID может быть, например, 598690 - person Birish; 13.03.2020
comment
Это странно. Скрипт у меня отлично работает. Вы запускаете скрипт из каталога, содержащего все эти файлы и каталоги, верно? - person Nitish; 14.03.2020

в Unix используйте команду ниже

find . -name '*508471*' -exec bash -c 'echo mv $0 ${0/508471/598690}' {} \;
person Sujit Dhamale    schedule 13.03.2020
comment
это не только один ID, есть 200 разных ID! С помощью написанной вами команды мне нужно вручную изменить идентификатор 200 раз. Мне нужна команда, которая перебирает все идентификаторы и перемещает их в каталог с тем же именем. - person Birish; 13.03.2020

Вы можете использовать этот цикл for из родительского каталога этих файлов и каталогов pdf:

for d in */; do
    compgen -G "*${d%/}*.pdf" >/dev/null && mv *"${d%/}"*.pdf "$d"
done

compgen -G используется для проверки, есть ли совпадение для данного глобуса или нет.

person anubhava    schedule 13.03.2020