คัดลอกโฟลเดอร์แบบเรียกซ้ำโดยไม่รวมบางโฟลเดอร์


197

ฉันพยายามเขียนสคริปต์ทุบตีง่าย ๆ ที่จะคัดลอกเนื้อหาทั้งหมดของโฟลเดอร์รวมถึงไฟล์และโฟลเดอร์ที่ซ่อนอยู่ในโฟลเดอร์อื่น แต่ฉันต้องการแยกโฟลเดอร์เฉพาะบางอย่างออก ฉันจะบรรลุสิ่งนี้ได้อย่างไร


1
ฉันจินตนาการถึงสิ่งที่ต้องการค้นหา -name * piped ไปที่ grep / v "แยกรูปแบบ" เพื่อกรองสิ่งที่คุณไม่ต้องการจากนั้น piped ไปที่ cp เพื่อทำสำเนา
i_am_jorf

1
ฉันพยายามทำบางอย่างเช่นนั้น แต่ไม่สามารถหาวิธีการใช้ cp กับ
ไพพ์

1
นี่น่าจะเป็นของผู้ใช้ระดับสูง คำสั่งที่คุณต้องการคือ xargs คุณสามารถทำบางอย่างเช่น tar สองอันเชื่อมต่อกันด้วยไพพ์
Kyle Butt

1
อาจจะล่าช้าและไม่ตอบคำถามได้อย่างถูกต้อง แต่นี่เป็นเคล็ดลับ: หากคุณต้องการยกเว้นเฉพาะเด็ก ๆ ในสารบบคุณสามารถใช้ประโยชน์จากการจับคู่รูปแบบ bash ได้เช่นcp -R !(dir1|dir2) path/to/destination
Boris D. Teoharov

1
โปรดทราบว่า!(dir1|dir2)ต้องextglobเปิดใช้รูปแบบ( shopt -s extglobเพื่อเปิดใช้)
Boris D. Teoharov

คำตอบ:


335

ใช้ 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 นั้นถูกรวมไว้ในลินุกซ์กระแสหลักทั้งหมดที่ฉันรู้จักรวมถึง RHEL, CentOS, Debian และ Ubuntu และฉันเชื่อว่ามันอยู่ใน FreeBSD เช่นกัน
siliconrockstar

1
สำหรับ distros ที่ได้จาก RHEL: yum install rsync หรือในรุ่นที่ใช้ Debian: apt-get install rsync นอกจากว่าคุณกำลังสร้างเซิร์ฟเวอร์ของคุณจากฐานแน่นอนบนฮาร์ดแวร์ของคุณเองนี่ไม่ใช่ปัญหา rsync จะถูกติดตั้งตามค่าเริ่มต้นในกล่อง Amazon EC2 ของฉันเช่นกันและกล่องของฉันจาก ZeroLag และ RackSpace
siliconrockstar

2
rsync ดูเหมือนว่าจะช้ามากเมื่อเทียบกับ CP? อย่างน้อยนี่คือประสบการณ์ของฉัน
Kojo

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 คุณผิด นี่คือทางออกที่ดีที่สุด มันทำงานตรงตามที่ OP ร้องขอและทำงานกับการติดตั้งเริ่มต้นของ * nix ส่วนใหญ่เช่น OS การทำการทดสอบและการเลิกทำได้ทันทีโดยไม่ต้องใช้ระบบไฟล์ (ในหน่วยความจำ) ค่าใช้จ่ายของ tar + untar นี้เล็กน้อย
AmokHuginnsson

@WouterDonders Tar เป็นค่าใช้จ่ายน้อยที่สุด มันไม่ใช้การบีบอัด
Kyle Butt

9

คุณสามารถใช้findกับ-pruneตัวเลือก

ตัวอย่างจากman find:

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

       คำสั่งนี้คัดลอกเนื้อหาของ / source-dir ไปยัง / dest-dir แต่ไม่ต้องสนใจ
       ไฟล์และไดเรกทอรีชื่อ. snapshot (และอะไรก็ได้ในนั้น) มันยัง
       ละเว้นไฟล์หรือไดเรกทอรีที่ชื่อลงท้ายด้วย ~ แต่ไม่รวมถึงไฟล์
       เต็นท์ โครงสร้าง -prune -o \ (... -print0 \) ค่อนข้างธรรมดา 
       แนวคิดนี่คือการแสดงออกก่อน -prune ตรงกับสิ่งที่
       จะถูกตัดแต่งกิ่ง อย่างไรก็ตามแอ็คชัน -prune จะส่งกลับค่าจริงดังนั้น
       ดังต่อไปนี้ - เพื่อให้แน่ใจว่าได้รับการประเมินทางด้านขวามือเท่านั้น
       ไดเร็กทอรีเหล่านั้นที่ไม่ได้ถูกตัด (เนื้อหาของการตัด
       ไดเรกทอรีที่ไม่ได้เยี่ยมชมดังนั้นเนื้อหาของพวกเขาจะไม่เกี่ยวข้อง)
       นิพจน์ทางด้านขวามือของ -o อยู่ในวงเล็บเท่านั้น
       เพื่อความชัดเจน โดยเน้นว่าการดำเนินการ -print0 เกิดขึ้นเท่านั้น
       สำหรับสิ่งที่ไม่ได้ถูกนำไปใช้กับพวกเขา เพราะว่า
       ค่าเริ่มต้น `และ 'เงื่อนไขระหว่างการทดสอบผูกให้แน่นกว่า -o สิ่งนี้
       เป็นค่าเริ่มต้นอย่างไรก็ตามวงเล็บช่วยในการแสดงว่าเกิดอะไรขึ้น
       บน.

อุปกรณ์ประกอบฉากสำหรับการค้นหาตัวอย่างที่มีความเกี่ยวข้องสูงโดยตรงจาก manpage
David M

ดูดีจริงๆ! นี้ยังมีอยู่ในเอกสารออนไลน์ น่าเสียดายที่cpioยังไม่ได้รับการบรรจุสำหรับ MSYS2
underscore_d

3

คุณสามารถใช้ tar พร้อมกับ - ยกเว้นตัวเลือกจากนั้นยกเลิกการเลือกที่ปลายทาง เช่น

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

ดู man page ของ tar สำหรับข้อมูลเพิ่มเติม


2

คล้ายกับแนวคิดของ Jeff (ยังไม่ทดลอง):

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 (จริง) สิ่งนี้จะหยุดคุณจากการคัดลอกหลายครั้ง
Eric Bringley

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

@underscore_d: จริงในการเข้าใจถึงปัญหาโดยเฉพาะในขณะที่ฉันไม่สามารถจำสิ่งที่ล้มเหลว :-(
go2null

หลายสิ่ง: (1) มันคัดลอกไฟล์หลายครั้งและ (2) ตรรกะยังคงคัดลอกไฟล์ที่จะถูกแยกออก วิ่งผ่านลูปโดยใช้ i = foo: มันจะถูกคัดลอก 3 ครั้งแทนที่จะเป็น 4 สำหรับไฟล์อื่น ๆ เช่น i = test.txt
Eric Bringley

1
ขอบคุณ @EricBringley สำหรับการชี้แจงข้อบกพร่องของคำตอบของ Steve (เขาบอกว่ามันยังไม่ผ่านการทดสอบ )
go2null
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.