Copia la cartella in modo ricorsivo, escludendo alcune cartelle


197

Sto cercando di scrivere un semplice script bash che copierà l'intero contenuto di una cartella, inclusi file e cartelle nascosti in un'altra cartella, ma voglio escludere determinate cartelle specifiche. Come ho potuto raggiungere questo obiettivo?


1
Immagino qualcosa come trovare. -name * reindirizzato a grep / v "exclude-pattern" per filtrare quelli non desiderati e quindi reindirizzato a cp per eseguire la copia.
i_am_jorf,

1
Stavo cercando di fare qualcosa del genere, ma non riuscivo a capire come usare cp con una pipe
trobrock

1
Questo dovrebbe probabilmente andare al superutente. Il comando che stai cercando è xargs. Potresti anche fare qualcosa come due catrame collegate da una pipa.
Kyle Butt

1
Forse è tardi e non risponde alla domanda in modo accurato, ma ecco un suggerimento: se si desidera escludere solo i figli immediati della directory, è possibile trarre vantaggio dalla corrispondenza del modello bash, ad esempiocp -R !(dir1|dir2) path/to/destination
Boris D. Teoharov

1
Si noti che il !(dir1|dir2)motivo extglobdeve essere attivato ( shopt -s extglobper attivarlo).
Boris D. Teoharov,

Risposte:


334

Usa rsync:

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

Si noti che l'utilizzo sourcee source/sono diversi. Una barra finale significa copiare il contenuto della cartella sourcein destination. Senza la barra finale, significa copiare la cartella sourcein destination.

In alternativa, se hai molte directory (o file) da escludere, puoi usare --exclude-from=FILE, dove si FILEtrova il nome di un file contenente file o directory da escludere.

--exclude può contenere anche caratteri jolly, ad esempio --exclude=*/.svn*


10
Suggerisco di aggiungere --dry-run per verificare quali file verranno copiati.
Loretoparisi,

1
@AmokHuginnsson - Quali sistemi stai usando? Rsync è incluso di default in tutte le distro principali di Linux che conosco, inclusi RHEL, CentOS, Debian e Ubuntu, e credo che sia anche in FreeBSD.
siliconrockstar

1
Per le distribuzioni derivate da RHEL: yum install rsync o su versioni basate su Debian: apt-get install rsync. A meno che tu non stia costruendo il tuo server dalla base assoluta sul tuo hardware, questo non è un problema. rsync è installato di default sui miei box Amazon EC2 e anche su quelli di ZeroLag e RackSpace.
siliconrockstar,

2
rsync sembra essere estremamente lento rispetto a cp? Almeno questa è stata la mia esperienza.
Kojo,

2
Ad esempio per ignorare la rsync -av --exclude='.git/' ../old-repo/ .
directory

40

Usa il catrame insieme a una pipa.

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

Puoi anche usare questa tecnica su ssh.


Questo approccio prima non serve a tarare la fonte di destinazione (ed esclude particolari directory nell'archivio) e poi a scriverlo sulla destinazione. Non consigliato!
Wouter Donders,

4
@Waldheri ti sbagli. Questa è la soluzione migliore Fa esattamente ciò che OP ha richiesto e funziona con l'installazione predefinita della maggior parte dei * nix come i SO. La taratura e l'annullamento vengono effettuati al volo senza artefatti del file system (in memoria), il costo di questo tar + untar è trascurabile.
AmokHuginnsson,

@WouterDonders Tar è un sovraccarico minimo. Non applica la compressione.
Kyle Butt,

9

È possibile utilizzare findcon l' -pruneopzione.

Un esempio da man find:

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

       Questo comando copia il contenuto di / source-dir in / dest-dir, ma omette
       file e directory denominati .snapshot (e qualsiasi cosa in essi). Anche
       omette i file o le directory il cui nome termina in ~, ma non il loro con-
       tende. Il costrutto -prune -o \ (... -print0 \) è abbastanza comune. Il
       l'idea qui è che l'espressione prima di -pruna corrisponde alle cose che sono
       essere potato. Tuttavia, l'azione -prune stessa restituisce true, quindi la
       che segue -o garantisce che il lato destro sia valutato solo per
       quelle directory che non sono state potate (il contenuto della potatura
       le directory non sono nemmeno visitate, quindi i loro contenuti sono irrilevanti).
       L'espressione sul lato destro di -o è solo tra parentesi
       per chiarezza. Sottolinea che l'azione -print0 ha luogo solo
       per cose che non avevano -prune applicato a loro. Perché il
       la condizione di default `e 'tra i test si lega più strettamente di -o, questo
       è comunque l'impostazione predefinita, ma le parentesi aiutano a mostrare cosa sta succedendo
       sopra.

Puntelli per localizzare un esempio altamente rilevante direttamente da una pagina di manuale.
David M,

Sembra davvero buono! Questo è disponibile anche nei documenti online . Sfortunatamente cpionon è stato ancora creato un pacchetto per MSYS2.
underscore_d

3

puoi usare tar, con l'opzione --exclude, e poi decomprimerlo nella destinazione. per esempio

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

vedere la pagina man di tar per maggiori informazioni


2

Simile all'idea di Jeff (non testato):

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

Mi dispiace, ma non capisco perché 5 persone hanno votato questo quando è stato certamente non testato e non sembra funzionare su un semplice test: ho provato questo in un sottodirettore /usr/share/iconse ho ottenuto immediatamente find: paths must precede expression: 22x22dove quest'ultimo è uno dei sotto-sotto . Il mio comando era find . -name * -print0 | grep -v "scalable" | xargs -0 -I {} cp -a {} /z/test/(ammetto, sono su MSYS2, quindi davvero /mingw64/share/icons/Adwaita, ma non riesco a vedere come sia colpa di 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

Non testato...


Questo non è corretto Alcuni problemi: come scritto, copierà un file che non dovrebbe essere escluso più volte (il numero di elementi da escludere che in questo caso è 4). Anche se si tenta di copiare 'pippo', il primo elemento nell'elenco di esclusione, verrà comunque copiato quando si arriva a x = bar e io è ancora pippo. Se insisti a farlo senza strumenti preesistenti (ad es. Rsync), sposta la copia in un'istruzione if al di fuori del ciclo "for x in ..." e fai in modo che il ciclo "for x ..." cambi l'istruzione logica in il file di copia if (true). Questo ti impedirà di copiare più volte.
Eric Bringley,

0

ispirato alla risposta di @ SteveLazaridis, che fallirebbe, ecco una funzione shell POSIX: basta copiare e incollare in un file chiamato cpxin yout $PATHe renderlo eseguibile ( chmod a+x cpr). [La fonte è ora gestita nel mio 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 "$@"

Esempio di utilizzo

EXCLUDE="
.git
my_secret_stuff
"
cpr "$HOME/my_stuff" "/media/usb" "$EXCLUDE"

Sembra inutile dire che la risposta di qualcuno "fallirebbe" senza spiegare cosa c'è che non va e come risolverlo ...
underscore_d

@underscore_d: vero, con il senno di poi, soprattutto perché non riesco a ricordare cosa non è riuscito :-(
go2null

Cose multiple: (1) copia i file più volte e (2) la logica copia ancora i file da escludere. Esegui i loop usando i = foo: verrà copiato 3 volte anziché 4 per qualsiasi altro file, ad es. I = test.txt.
Eric Bringley,

1
grazie @EricBringley per aver chiarito le carenze della risposta di Steve. (Ha detto che non è stato testato .)
go2null
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.