Копіюйте папку рекурсивно, виключаючи деякі папки


197

Я намагаюся написати простий скрипт bash, який буде копіювати весь вміст папки, включаючи приховані файли та папки, в іншу папку, але я хочу виключити певні конкретні папки. Як я міг цього досягти?


1
Я уявляю щось на кшталт знахідки. -name * piped to grep / v "виключити шаблон", щоб відфільтрувати ті, які ви не хочете, а потім піп на cp, щоб зробити копію.
i_am_jorf

1
Я намагався зробити щось подібне, але не міг зрозуміти, як використовувати cp з трубою
trobrock

1
Це, мабуть, має перейти до суперкористувача. Команда, яку ви шукаєте, - xargs. Ви також можете зробити щось на зразок двох дьогтів, з'єднаних трубою.
Кайл Батт

1
Можливо, вона пізно, і це не відповідає точно на питання, але ось підказка: Якщо ви хочете виключити лише безпосередніх дітей із каталогу, ви можете скористатись узгодженням схеми bash, наприкладcp -R !(dir1|dir2) path/to/destination
Борис Д. Теохаров

1
Зауважте, що !(dir1|dir2)шаблон потрібно extglobввімкнути ( shopt -s extglobщоб увімкнути його).
Борис Д. Теохаров

Відповіді:


334

Використовуйте 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*


10
Я пропоную додати --dry-run, щоб перевірити, які файли будуть скопійовані.
loretoparisi

1
@AmokHuginnsson - Які системи ви використовуєте? Rsync включений за замовчуванням у всі відомі мені дистрибутиви Linux, включаючи RHEL, CentOS, Debian та Ubuntu, і я вважаю, що це також у FreeBSD.
siliconrockstar

1
Для дистрибутивів, отриманих RHEL: yum install rsync або на випусках на основі Debian: apt-get install rsync. Якщо ви не будуєте сервер з абсолютної бази на власному апаратному забезпеченні, це не проблема. rsync встановлюється за замовчуванням на моїх коробках Amazon EC2, а також на моїх коробках із ZeroLag та RackSpace.
siliconrockstar

2
rsync здається надзвичайно повільним порівняно з CP? Принаймні, це був мій досвід.
Коджо

2
Наприклад, щоб ігнорувати git dir:rsync -av --exclude='.git/' ../old-repo/ .
nycynik

40

Використовуйте дьоготь разом з трубою.

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

Ви навіть можете використовувати цю техніку через ssh.


Цей підхід невиправдано спочатку розбирає цільове джерело (і виключає конкретні каталоги в архіві), а потім знімає його з цілі. Не рекомендовано!
Wouter Donders

4
@Waldheri ви помиляєтесь. Це найкраще рішення. Він робить саме те, що вимагав ОП, і працює при встановленні за замовчуванням більшості * nix подібних ОС. Відміняння та знімання даних відбувається на ходу без артефакту файлової системи (в пам'яті), вартість цього tar + untar незначна.
AmokHuginnsson

@WouterDonders смола мінімальна накладні витрати. Він не застосовує стиснення.
Кайл Батт

9

Ви можете використовувати findз -pruneопцією.

Приклад з man find:

       cd / source-dir
       знайти. -name .snapshot -prune -o \ (\! -name * ~ -print0 \) |
       cpio -pmd0 / dest-реж

       Ця команда копіює вміст / source-dir в / dest-dir, але пропускає
       файли та каталоги з назвою .snapshot (і все, що в них). Він також
       опускає файли чи каталоги, ім'я яких закінчується на ~, але не їх
       намети. Конструкція -prune -o \ (... -print0 \) є досить поширеною. The
       Ідея тут полягає в тому, що вираз до -prune відповідає речам, які є
       підрізати. Однак сама дія -prune повертає істину, тому
       далі -o гарантує, що права частина оцінюється лише за
       ті каталоги, які не були обрізані (вміст обрізаного
       каталоги навіть не відвідуються, тому їх зміст не має значення).
       Вираз праворуч від -o є лише в дужках
       для наочності. Він підкреслює, що дія -print0 відбувається лише
       для речей, у яких не було застосовано до них. Тому що
       умова `та 'за умовчанням між тестами пов'язується більш щільно, ніж -o, це
       за замовчуванням все одно, але дужки допомагають показати, що відбувається
       на.

Реквізити для пошуку дуже релевантного прикладу безпосередньо з сторінки сторінки.
David M

Виглядає добре! Це також доступне в онлайн-документах . На жаль, cpioще не упаковано для MSYS2.
підкреслюй_d

3

ви можете скористатись tar, з опцією --exclude, а потім зніміть його в місці призначення. напр

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

для отримання додаткової інформації див


2

Схожа на ідею Джеффа (неперевірена):

find . -name * -print0 | grep -v "exclude" | xargs -0 -I {} cp -a {} destination/

Вибачте, але я дійсно не розумію, чому 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)
underscore_d

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

Неперевірений ...


Це неправильно. Кілька проблем: як написано, він буде копіювати файл, який не повинен бути вилучений кілька разів (кількість елементів, які слід виключити, у цьому випадку 4). Навіть якщо ви спробуєте скопіювати 'foo', перший елемент у списку виключення, він все одно буде скопійований, коли ви перейдете до x = bar, і я все ще foo. Якщо ви наполягаєте на цьому без попередніх інструментів (наприклад, rsync), перемістіть копію в оператор if за межами циклу "for x in ..." і зробіть цикл "for x ..." змінити логічний вислів у файл копіювання if (true). Це не дозволить вам копіювати кілька разів.
Ерік Брінглі

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's 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"

Здається, не можна було б сказати, що відповідь когось "провалиться", не пояснюючи, що з цим не так, і як це виправити ...
підкреслюю

@underscore_d: правда, заздалегідь, esp, оскільки я не можу зараз згадати, що не вдалося :-(
go2null

Багато речей: (1) він копіює файли кілька разів і (2) логіка все ще копіює файли, які потрібно виключити. Виконайте цикл за допомогою i = foo: він буде скопійований 3 рази замість 4 для будь-якого іншого файлу, наприклад, i = test.txt.
Ерік Брінглі

1
дякую @EricBringley за роз’яснення недоліків відповіді Стіва. (Він сказав, що це було неперевірено .)
go2null
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.