LinuxForum.Fans
Тема

Рекурсивное перемещение файлов по маске с сохранением структуры

Автор: NordWest 30 сообщений 11777 просмотров
#140639

Здравствуйте.
Нужен способ перемещения файлов, соответствующих набору масок с регулярными выражениями, из одной директории в другую, причем рекурсивно и с сохранением структуры папок. В винде это делается очень удобно в TotalCommander командой переместить. В Линуксе (Debian 5) это стало проблемой. Попробовал Krusader, Gnome Commander и Tux Commander, но в них такой опции нет.
Пока лучшее что получилось - сделать такой скрипт:

text

#! /bin/sh
find $1 -name "*.v21.log" -o -name "*.v22.log" -o -name "*.v23.log" -o -name "*.aver.log" -exec mv {} $2 \;

Но не создается структура каталогов исходной папки. Опций таких я не нашел. Посоветуете что-нибудь?

#140777

Можно пояснить на примере, что именно требуется?

#140831
NordWest написал:
Опций таких я не нашел. Посоветуете что-нибудь?

эх... есть такая опция. называется она... никак она не называется :)

[console]$ cp -Rv z dd
«z» -> «dd/z»
«z/send-spam» -> «dd/z/send-spam»
«z/spam-adr.txt» -> «dd/z/spam-adr.txt»
«z/1 3» -> «dd/z/1 3»
«z/file_list» -> «dd/z/file_list»
«z/tttt.txt» -> «dd/z/tttt.txt»
«z/inodes» -> «dd/z/inodes»
«z/1 2» -> «dd/z/1 2»
«z/fp.c» -> «dd/z/fp.c»
«z/1» -> «dd/z/1»
«z/message» -> «dd/z/message»
«z/fp» -> «dd/z/fp»
[/console]

не то?

#140832

а с find будет так:
[console]$ find -regex '.*1.*' -print0 | xargs -0 cp --parents -Rv -t a
«./1 3» -> «a/./1 3»
«./1 2» -> «a/./1 2»
./dd -> a/./dd
./dd/z -> a/./dd/z
«./dd/z/1 3» -> «a/./dd/z/1 3»
«./dd/z/1 2» -> «a/./dd/z/1 2»
«./dd/z/1» -> «a/./dd/z/1»
./dd/z/zzz -> a/./dd/z/zzz
«./dd/z/zzz/1 3» -> «a/./dd/z/zzz/1 3»
«./dd/z/zzz/1 2» -> «a/./dd/z/zzz/1 2»
«./dd/z/zzz/1» -> «a/./dd/z/zzz/1»
«./1» -> «a/./1»
./z -> a/./z
«./z/1 3» -> «a/./z/1 3»
«./z/1 2» -> «a/./z/1 2»
«./z/1» -> «a/./z/1»
[/console]

оно?

#140833

потом удалить надо всё, что в регепсе за исключением того, что скопировали
[console]$ find -regex '.*1.*' ! -regex "\./a/.*" -delete[/console]

PS: настоятельно рекомендую прочитать man regex
ну и man cp конечно...

#140834
NordWest написал:
-name "*.v21.log" -o -name "*.v22.log" -o -name "*.v23.log" -o -name "*.aver.log"
text
-regex '.*\.\(v2[123]\|aver\)\.log'

не?

#141022

Спасибо за ответы. Что-то никак с подпиской на темы не разберусь, только сегодня увидел отклики.

В общем, добиться нужного результата получилось таким вот образом:

text
find $1 -regex '.*\.\(v2[123]\|aver\)\.log' -exec cp --parents -Rv {} $2 \;
find $1 -regex '.*\.\(v2[123]\|aver\)\.log' -delete

Т.е. сначала копируется с созданием в целевой папке $2 структуры каталогов, за что спасибо --parents похоже. А вторым действием ищутся те же файлы и удаляются. В принципе, если добавить к cp ключ -f, то потери данных опасаться не стоит. Может и не очень изящно, но работает. Большое спасибо!


А вот ещё вопрос. find придется запускать из директории $1 и тут возникает опасность с относительными путями в $1 и $2. Как можно перевести относительный путь в абсолютный?

#141040
NordWest написал:
Как можно перевести относительный путь в абсолютный?

[console]$ readlink -f ../../disk-1/slax/
/media/disk-1/slax
[/console]

не забывайте заключать переменные в "двойные кавычки", а параметры без переменных в 'одиночные'

а то наступите на такие грабли:
[console]root@ksu:/media/disk/Music/1988# /bin/ls 1988-05*
1988-05 - Один день Ивана Денисовича.mp3
root@ksu:/media/disk/Music/1988# N=`/bin/ls 1988-05*`
root@ksu:/media/disk/Music/1988# ls $N
/bin/ls: невозможно получить доступ к 1988-05: Нет такого файла или каталога
/bin/ls: невозможно получить доступ к -: Нет такого файла или каталога
/bin/ls: невозможно получить доступ к Один: Нет такого файла или каталога
/bin/ls: невозможно получить доступ к день: Нет такого файла или каталога
/bin/ls: невозможно получить доступ к Ивана: Нет такого файла или каталога
/bin/ls: невозможно получить доступ к Денисовича.mp3: Нет такого файла или каталога
[/console]

как видите, shaell разрезала имя файла с пробелами так, что команда поняла это не как одно имя, а как несколько.
а вот так работает
[console]root@ksu:/media/disk/Music/1988# ls "$N"
1988-05\ -\ Один\ день\ Ивана\ Денисовича.mp3*
[/console]

NordWest написал:
за что спасибо --parents похоже.

--parents просто копирует не файл x.txt, а файл ./dir/x.txt
получается, что копируется файл target/./dir/x.txt или, что тоже самое target/dir/x.txt, ибо точка - это другое имя этого-же каталога (хардлинк).

Кстати, точка именно для этого и применяется.

#151186

В результате оказалось на много проще и удобнее установить TotalCommander под wine. Как то сразу идея не пришла в голову почему-то. Он там с перезаписью правда что-то мутит, но всё равно лучше чем скриптами заморачиваться. Ну зато со скриптами кое-какие вопросы с вашей помощью разобрал, тоже польза.

#151316
NordWest написал:
В результате оказалось на много проще и удобнее установить TotalCommander под wine.

хороший костыль на первое время :)

NordWest написал:
но всё равно лучше чем скриптами заморачиваться.

такие вопросы решаются простым одностроком. вот только я не очень понимаю, ЗАЧЕМ вообще решать такую задачу? если это часть скрипта - то скриптом таки проще. А именно как вы пишете - облазить каталоги, найти файлы, а потом их копировать ещё и с сохранением... Есть ещё один путь:

[console]( cd dir1; tar -cf - --exclude MASK * ) | ( cd dir2; tar -xf - )[/console]

это переносит(точнее копирует) файлы из dir1 в dir2. Сам архив нигде никогда не сохраняется, т.ч. не бойтесь, я так сотни гигов переносил.

#176533

Позвольте снова затронуть тему. Появилось более существенное применение подобного скрипта.
Значит у меня есть директория с подпапками и файлами, которые требуется переместить.
Т.е.:
/path2inidir/folder01/file1.arj
/path2inidir/folder01/file2.arj
/path2inidir/folder02/file1.arj
/path2inidir/folder02/file2.arj

Мне нужно переместить только файлы с расширением dat1 в другую папку /path2resdir с сохранением структуры:
/path2resdir/folder01/file1.arj
/path2resdir/folder01/file2.arj
/path2resdir/folder02/file1.arj
/path2resdir/folder02/file2.arj

Если я делаю так:

text
find $1 -regex '.*\(average\|raw\)\.arj' -exec cp --parents -rv {} $2 \;

То он создает такую структуру папок:
/path2resdir/path2inidir/folder01/file1.arj
...

Пробовал этот вариант:
[console]( cd dir1; tar -cf - --exclude MASK * ) | ( cd dir2; tar -xf - )[/console]

Но тут не понятно как с маской быть. Указать *.arj *, так он мне arj мои и исключит. Там ключа --include нет случаем? Я в описании не нашел.

#176848

Получилось! :)
Уверен, что очень криворуко, и есть более простой вариант, но работает.

text

#!/bin/bash
for file in `find $1 -regex '.*\(average\|raw\)\.arj' -print`
do 
DIR=$2${file#$1}
DIR=${DIR%/*}
mkdir -p $DIR
cp -R ${file}  $DIR
done

Жаль, что в винде это делается одной командой.

text
xcopy %1*.arj %2 /E/Y
#178279
NordWest написал:
Жаль, что в винде это делается одной командой.

:( :[
[console]$ cp -v --parent **/*p.png target/[/console]

Показать
[console]
common -> target/common
common/extensions -> target/common/extensions
common/extensions/docs -> target/common/extensions/docs
common/extensions/docs/images -> target/common/extensions/docs/images
common/extensions/docs/images/index -> target/common/extensions/docs/images/index
«common/extensions/docs/images/index/html5app.png» -> «target/common/extensions/docs/images/index/html5app.png»
common/extensions/docs/images/overview -> target/common/extensions/docs/images/overview
«common/extensions/docs/images/overview/browser-action-with-popup.png» -> «target/common/extensions/docs/images/overview/browser-action-with-popup.png»
«common/extensions/docs/images/overview/flash-app.png» -> «target/common/extensions/docs/images/overview/flash-app.png»
d3 -> target/d3
d3/d4 -> target/d3/d4
d3/d4/app -> target/d3/d4/app
d3/d4/app/theme -> target/d3/d4/app/theme
«d3/d4/app/theme/back_p.png» -> «target/d3/d4/app/theme/back_p.png»
«d3/d4/app/theme/browser_action_p.png» -> «target/d3/d4/app/theme/browser_action_p.png»
«d3/d4/app/theme/browser_actions_overflow_p.png» -> «target/d3/d4/app/theme/browser_actions_overflow_p.png»
«d3/d4/app/theme/browser_actions_overflow_win_p.png» -> «target/d3/d4/app/theme/browser_actions_overflow_win_p.png»
«d3/d4/app/theme/close_bar_p.png» -> «target/d3/d4/app/theme/close_bar_p.png»
«d3/d4/app/theme/download_button_center_bottom_p.png» -> «target/d3/d4/app/theme/download_button_center_bottom_p.png»
«d3/d4/app/theme/download_button_center_middle_p.png» -> «target/d3/d4/app/theme/download_button_center_middle_p.png»
«d3/d4/app/theme/download_button_center_top.png» -> «target/d3/d4/app/theme/download_button_center_top.png»
[/console]

hint: [console]$ shopt globstar
globstar on
[/console]

NordWest написал:
Пробовал этот вариант:
Консоль:

( cd dir1; tar -cf - --exclude MASK * ) | ( cd dir2; tar -xf - )

Но тут не понятно как с маской быть. Указать *.arj *, так он мне arj мои и исключит. Там ключа --include нет случаем? Я в описании не нашел.

эх...

звёздочку меняйте на **/*{average,raw}.arj

#178280

и да, man bash

man bash написал:
globstar
If set, the pattern ** used in a pathname expansion context will match a files and zero or
more directories and subdirectories. If the pattern is followed by a /, only directories
and subdirectories match.

info coreutils 'cp invocation'

#178281
NordWest написал:
xcopy %1*.arj %2 /E/Y

а как **/*{average,raw}.arj :P

#178296
drBatty написал:
hint:

У меня "globstar off".

Если перейти в папку, из которой нужно копировать, то это сработает:
[console]$ cp -v --parent **/*.arj target/[/console]

Если без перехода и с путем к исходной папке, так например:
[console]$ cp -v --parent $1**/*.arj target/[/console]
Будет снова "/path2resdir/path2inidir/folder01/file1.arj"
Конечно можно переходить -> копировать -> возвращаться.

#178310
NordWest написал:
У меня "globstar off".

дык сделайте on.
hint: help shopt

NordWest написал:
то это сработает:

нет.
подкаталоги не пойдут.

NordWest написал:
cp -v --parent $1**/*.arj target/

[console]( cd "$1" && cp -v --parent **/*.arj target/ )[/console]
не?
ВСЕ операции в Linux выполняются в рабочем каталоге. Управлять рабочим каталогом из какого-то левого - ИМХО маздайный бред. Кстати, в TC&NC тоже есть текущий каталог.

PS: внутри скрипта скобки не нужны:

text
#!/bin/bash
shopt -s globstar
cd "$1"
cp -v --parent **/*.arj target/
#178320

а если что-то подобное соорудить:
[console]#!/bin/bash
find <фильтр> | while read FILE; do
TARGET_FILE_DIR="${TARGET_DIR}/$(echo ${FILE}| sed -e 's!\(.*/\).*$!\1!')"
mkdir -p $TARGET_FILE_DIR
cp FILE $TARGET_FILE_DIR
done
[/console]

#178429
Fat-Zer написал:
а если что-то подобное соорудить:

а зачем?

Кстати, sed тут во первых неправильно применяется, во вторых просто не нужна.
Как и вся та строчка.

#178452
drBatty написал:
а зачем?

не знаю... мне так нравится...

drBatty написал:
Кстати, sed тут во первых неправильно применяется, во вторых просто не нужна.

нуда... скобки забыл... fixed...
как без него?

drBatty написал:
Как и вся та строчка.

о_О

Войдите или зарегистрируйтесь, чтобы ответить.