Рекурсивно копировать папку, исключая некоторые папки

159

Я пытаюсь написать простой bash script, который скопирует все содержимое папки, включая скрытые файлы и папки, в другую папку, но я хочу исключить определенные определенные папки. Как я мог достичь этого?

  • 1
    Я представляю что-то вроде поиска. -name * передается в grep / v "exclude-pattern", чтобы отфильтровать ненужные файлы, а затем передается в cp, чтобы сделать копию.
  • 1
    Я пытался сделать что-то подобное, но не мог понять, как использовать cp с каналом
Показать ещё 4 комментария
Теги:
scripting

7 ответов

270
Лучший ответ

Использовать rsync:

rsync -av --exclude='path1/to/exclude' --exclude='path2/to/exclude' source destination

Обратите внимание, что использование source и source/ отличается. Конечная косая черта означает скопировать содержимое папки source в destination. Без конечной косой черты это означает, что скопировать папку source в destination.

В качестве альтернативы, если у вас есть много каталогов (или файлов) для исключения, вы можете использовать --exclude-from=FILE, где FILE - это имя файла, содержащего файлы или каталоги для исключения.

--exclude может также содержать подстановочные знаки, такие как --exclude=*/.svn*

  • 38
    обратите внимание, 'path1 / to / exclude' и т. д. являются подкаталогом "source".
  • 0
    Если вы, как и я, хотите рекурсивно скопировать всю структуру и сохранить только определенный тип файлов, вы также можете использовать rsync: rsync -rav --include = * / --include = " .txt" --exclude = test / mytest Это возьмет все файлы .txt из исходного каталога и скопирует их в целевой каталог со структурой без изменений.
Показать ещё 12 комментариев
32

Используйте tar вместе с трубой.

cd /source_directory
tar cf - --exclude=dir_to_exclude . | (cd /destination && tar xvf - )

Вы даже можете использовать эту технику через ssh.

  • 0
    При таком подходе вначале копируется целевой источник (и исключаются отдельные каталоги в архиве), а затем распаковывается его в целевой папке. Не рекомендуется!
  • 3
    @ Вальдхери, ты не прав. Это лучшее решение. Он выполняет именно то, что запрашивал OP, и работает при установке по умолчанию большинства * nix-подобных ОС. Тарирование и распаковка выполняются на лету без артефакта файловой системы (в памяти), стоимость этого tar + untar незначительна.
7

Вы можете использовать find с опцией -prune.

Пример из man find:

       cd /source-dir
       find . -name .snapshot -prune -o \( \! -name *~ -print0 \)|
       cpio -pmd0 /dest-dir

       This command copies the contents of /source-dir to /dest-dir, but omits
       files  and directories named .snapshot (and anything in them).  It also
       omits files or directories whose name ends in ~,  but  not  their  con‐
       tents.  The construct -prune -o \( ... -print0 \) is quite common.  The
       idea here is that the expression before -prune matches things which are
       to  be  pruned.  However, the -prune action itself returns true, so the
       following -o ensures that the right hand side  is  evaluated  only  for
       those  directories  which didn't get pruned (the contents of the pruned
       directories are not even visited, so their  contents  are  irrelevant).
       The  expression on the right hand side of the -o is in parentheses only
       for clarity.  It emphasises that the -print0 action  takes  place  only
       for  things  that  didn't  have  -prune  applied  to them.  Because the
       default `and' condition between tests binds more tightly than -o,  this
       is  the  default anyway, but the parentheses help to show what is going
       on.
  • 0
    Реквизиты для поиска очень актуального примера прямо из man-страницы.
  • 0
    Выглядит действительно хорошо! Это также доступно в онлайн-документах . К сожалению, cpio еще не был упакован для MSYS2.
5

Подобно идее Джеффа (непроверенный):

find . -name * -print0 | grep -v "exclude" | xargs -0 -I {} cp -a {} destination/
  • 0
    Извините, но я действительно не понимаю, почему 5 человек проголосовали против этого, когда он был признан непроверенным и, кажется, не работает в простом тесте: я попробовал это в поддиректории /usr/share/icons и сразу же find: paths must precede expression: 22x22 где последний является одним из подкаталогов в нем. Моя команда была find . -name * -print0 | grep -v "scalable" | xargs -0 -I {} cp -a {} /z/test/ (по общему признанию, я нахожусь на MSYS2, поэтому действительно в /mingw64/share/icons/Adwaita , но я не могу понять, какова ошибка MSYS2)
2

вы можете использовать tar, с параметром --exclude, а затем развернуть его в пункте назначения. например,

cd /source_directory
tar cvf test.tar --exclude=dir_to_exclude *
mv test.tar /destination 
cd /destination  
tar xvf test.tar

см. справочную страницу tar для дополнительной информации

0

вызванный ответом @SteveLazaridis, который потерпит неудачу, вот функция оболочки POSIX - просто скопируйте и вставьте в файл с именем cpx в yout $PATH и сделайте его исполняемым (chmod a+x cpr). [Источник теперь поддерживается в GitLab.

#!/bin/sh

# usage: cpx [-n|--dry-run] "from_path" "to_path" "newline_separated_exclude_list"
# limitations: only excludes from "from_path", not it subdirectories

cpx() {
# run in subshell to avoid collisions
(_CopyWithExclude "$@")
}

_CopyWithExclude() {
case "$1" in
-n|--dry-run) { DryRun='echo'; shift; } ;;
esac

from="$1"
to="$2"
exclude="$3"

$DryRun mkdir -p "$to"

if [ -z "$exclude" ]; then
    cp "$from" "$to"
    return
fi

ls -A1 "$from" \
    | while IFS= read -r f; do
    unset excluded
    if [ -n "$exclude" ]; then
    for x in $(printf "$exclude"); do
        if [ "$f" = "$x" ]; then
        excluded=1
        break
        fi
    done
    fi
    f="${f#$from/}"
    if [ -z "$excluded" ]; then
    $DryRun cp -R "$f" "$to"
    else
    [ -n "$DryRun" ] && echo "skip '$f'"
    fi
done
}

# Do not execute if being sourced
[ "${0#*cpx}" != "$0" ] && cpx "$@"

Пример использования

EXCLUDE="
.git
my_secret_stuff
"
cpr "$HOME/my_stuff" "/media/usb" "$EXCLUDE"
  • 0
    Кажется бесполезным говорить, что чей-то ответ «потерпит неудачу», не объяснив, что с ним не так и как это исправить ...
  • 0
    @underscore_d: правда, задним числом, особенно если я не могу вспомнить, что не удалось :-(
Показать ещё 2 комментария
0
EXCLUDE="foo bar blah jah"                                                                             
DEST=$1

for i in *
do
    for x in $EXCLUDE
    do  
        if [ $x != $i ]; then
            cp -a $i $DEST
        fi  
    done
done

Непроверено...

  • 0
    Это неверно Несколько проблем: Как написано, он скопирует файл, который не должен быть исключен несколько раз (количество исключаемых элементов, которое в данном случае равно 4). Даже если вы попытаетесь скопировать 'foo', первый элемент в списке исключений, он все равно будет скопирован, когда вы доберетесь до x = bar, а я все еще foo. Если вы настаиваете на том, чтобы делать это без уже существующих инструментов (например, rsync), переместите копию в оператор if вне цикла «for x in ...» и сделайте так, чтобы цикл «for x ...» изменил логический оператор в if (true) копия файла. Это остановит вас от копирования несколько раз.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню