Как в linux консоли скопировать файлы и папки исключая некоторые из них по регулярному выражению

    Здравствуйте, хабражители.

    Не судите строго, я новичёк в linux-е и это мой первый пост на хабре, но может кому-то похожему на меня он окажется полезным.

    Столкнулся с необходимостью решить задачу, описанную в заголовке. Готового решения не нашёл. Написал sh скрипт (cpexclude.sh), который сначала копирует всё, а потом удаляет лишнее:

    #!/bin/bash
    if [ $# -lt 3 ] ; then
    	echo "cpexclude usage: pathFrom pathTo excludeRegex"
    	exit 0
    fi
    pathFrom=$1
    pathTo=$2
    excludeRegex=$3
    # Copy everything
    echo `cp -a $pathFrom/. $pathTo`
    # Delete by excludeRegex
    echo `find $pathTo -regex $excludeRegex -delete`
    

    Добавил его в ~/.bashrc

    alias cpexclude='/path/to/cpexclude.sh'
    

    Пользуюсь время от времени.
    Если у кого-нибудь на уме есть более приемлемое решение, милости прошу.

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

    Upd2. В комментарих подсказали:
    • rsync -r --exclude=PATTERN from/ to/
      

    • SRC=~/work/soruce; DEST=~/work/test; REGEXP=js; pushd $SRC; for I in $(find ./ | grep -v "$REGEXP"); do if [ -d $I ]; then mkdir $DEST/$I; else cp $I $DEST/$I; fi; done; popd 2>/dev/null
      

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 43

      +4
      Хм, а если

      ls folder | grep -v [regexp] | xargs cp -t destination

      где:
      folder— исходная директория
      destination — конечная
      [regexp] — собственно, регулярка

      Флаг -v у grep делает следующее, если кто-нибудь запамятовал (выдержка из man):
      -v, --invert-match Selected lines are those not matching any of the specified patterns.
        0
        Вы пробовали запускать? Чёто у меня оно не хочет работать:
        ls /web/test/www | grep -v .*\.svn.* | xargs cp -t /web/test/tmp
        cp: cannot stat `app': No such file or directory
        cp: cannot stat `libraries': No such file or directory
        cp: cannot stat `public': No such file or directory
        
          +5
          Для текущей директории запустил:

          ls $1 | grep -v file1 | xargs cp $1 destination/$1

          Успешно переместило все файлы, кроме file1 в подпапку destination.

          Ваше решение слишком громоздко, а «сначала копирует всё, а потом удаляет лишнее» это лишние телодвижения.
            0
            Моё решение обрабатывает не толь файлы, но и все папки, включая подпапки.
              0
              Вообще моей изначальной целью было создание копии проекта, избавляясь от директорий .svn и не обращаясь при этом к репозиторию. По этой причине была необходимость обрабатывать и субдиректории.
                +2
                А разве svn export не подходит?
                  0
                  Мне не очень подходило, т.к. нужна была локальная копия с локальными изменениями.
                    +1
                    Откройте для себя бранчи и D-RCS, например, git.
              +2
              А в кавычки кто брать regexp будет? И этот человек пишет статью, состоящую целиком и полностью из одного скрипта. Вы бы хоть не позорились ужасным синтаксисом и не соблюдением элементарных правил!
                –2
                Понимаю, Вы — «системный администратор Linux / Windows»

                А я — … новичёк в linux-е и это мой первый пост на хабре, но может кому-то похожему на меня он окажется полезным…

                Исходя из вышесказанного, могли бы научить «как правильно», а не наезжать.
                  +3
                  Пожалуйста, вот в шапке этой темы на одном форуме есть уйма полезных ссылок. По прядку переходите по каждой ссылке и вдумчиво изучаете.
                    +2
                    Спасибо, Учитель!
                    0
                    мне вот отсюда много пригодилось
                    легко находится ищется по ключевым «advanced bash scripting guide»
                      0
                      Спасибо, почитаю на досуге.
                +1
                вместо ls folder надо просто написать find folder, это и будет решением задачи. find выводить файлы, рекурсивно просматривая каталоги.
                  0
                  find folder | grep -v [regexp] | xargs cp -t destination
                    +1
                    • не сохраняется структура папок
                    • не переписываются подпапки
                    0
                    Да, вы правы. find как раз подходит для решения задачи ТС.
                      0
                      Проблема еще не решена. cp ругается на каталоги, но флаг -r использовать нельзя, потому что он тогда скопирует ненужные файлы, а пропускать каталоги нельзя, потому что пользователь хочет скопировать пустые каталоги тоже.
                        0
                        Еще есть другая проблема. Если писать не относительный путь в find, то в целевом каталоге будут создаваться каталоги, соответствующие полному пути исходных файлов.
                          0
                          Универсальное решение:

                          SRC=~/work/soruce; DEST=~/work/test; REGEXP=js; pushd $SRC; for I in $(find ./ | grep -v "$REGEXP"); do if [ -d $I ]; then mkdir $DEST/$I; else cp $I $DEST/$I; fi; done; popd 2>/dev/null

                          PS. прошу прощения, что наплодил комментов (мысли приходят не сразу) :)
                            0
                            Не универсально:

                            find ./
                            

                            У Вас видимо текущей директорией является ~/work/soruce
                            По этому у Вас и работает
                            Если вы попробуете сменить текущую директорию, то повалится
                              +1
                              pushd — для временного перехода в нужную папку. внимательней ;)
                                +1
                                Да, Вы правы.
                          0
                          Решение для копирования файлов, начиная от текущего каталога

                          DEST=~/work/test; REGEXP=js; for I in $(find ./ | grep -v "$REGEXP"); do if [ -d $I ]; then mkdir $DEST/$I; else cp $I $DEST/$I; fi; done 2>/dev/null
                            0
                            DEST=~/work/test; REGEXP=js; find ./ | grep -v "$REGEXP" | cpio -pd $DEST
                    0
                    cp -r path1/[^regexp] path2
                    
                    не пойдёт?
                      0
                      Не понимает регулярное выражение
                      0
                      Но ведь вы тоже решили задачу, отличную от заявленной. А если у меня в папке есть пара гигабайтных файлов, которые я копировать не хочу, и для которых в destination места не хватит?
                        0
                        Я с этим и не спорю. Сам думаю о том, что нужно более «прямое» решение.
                        Если у кого-нибудь на уме есть более приемлемое решение, милости прошу.

                        +11
                        А «rsync --exclude=REGEX ...» чем не устраивает?
                          +5
                          Вот так работает:

                          rsync -r --exclude=PATTERN from/ to/
                          

                          Спасибо!
                            +1
                            Если нужно просто убрать папки */.svn, */.cvs и похожие, то достаточно использоваться rsync -C from/ to/
                            0
                            тем что его эксклуды — не регулярки
                              0
                              Ну, может там и не полноценные регулярные выражения, но в большинстве реальных случаев этого будет достаточно.
                            0
                            ну или аналогично rsync
                            tar cf — --exclude=PATTERN * | tar xf — -C newdir

                            думаю на www.commandlinefu.com можно еще десяток способов найти
                              +1
                              ну и еще общий вариант — сделать список фалов, проредить его грепом и скормить как источник того что нужно копировать любой из программ
                                +1
                                #!/bin/bash
                                if [ $# -lt 3 ] ; then
                                    echo "cpexclude usage: pathFrom pathTo excludeRegex"
                                    exit 0
                                fi
                                pathFrom="$1"
                                pathTo="$2"
                                excludeRegex="$3"
                                # Copy everything
                                find "$pathFrom" -type f -not -regex "$excludeRegex" | while read A ; do {
                                [[ -d "$pathTo/${A%/*}" ]] || mkdir -p "$pathTo/${A%/*}"
                                cp -a "$A" "$pathTo/${A%/*}"
                                }
                                done
                                
                            • UFO just landed and posted this here
                              • UFO just landed and posted this here
                                0
                                Вообще есть штатное решение
                                find . ! -iregex '.*gz' -exec cp -a '{}' \;

                                Это плохой пример чисто для наглядности, плохой потому что надо сначала копировать структуру директорий а потом фаилы по регекспу. Но идея думаю понятна.
                                  0
                                  А еще я тут недавно спрашивал в QA есть ли ресурс для bad practice по администрированию.
                                  Вот собственно с этого топика пожно начать наполнять подобный блог на хабре.

                                  Автору как новичку простительно ошибаться — видно что мозг работал просто рано остановился, но остальные должны увидеть предупреждение «НЕ ДЕЛАЙ ТАК» прежде чем начнут использовать этот код.
                                    0
                                    На всякий случай напишу полное решение
                                    find plpbt-5.0.14/ -type d -exec mkdir -p dest/'{}' \; find plpbt-5.0.14/ -type f ! -iregex '.*iso' -exec cp '{}' dest/'{}' \;

                                Only users with full accounts can post comments. Log in, please.