2010-02-03 7 views
162

J'essaye d'écrire un script bash simple qui va copier le contenu entier d'un dossier comprenant des dossiers et des dossiers cachés dans un autre dossier, mais je veux exclure certains dossiers spécifiques. Comment pourrais-je y parvenir?Copier le dossier de manière récursive, excluant certains dossiers

+1

j'imagine quelque chose comme trouver. -name * redirige vers grep/v "exclude-pattern" pour filtrer ceux que vous ne voulez pas, puis redirige vers cp pour faire la copie. –

+1

J'essayais de faire quelque chose comme ça, mais je ne pouvais pas comprendre comment utiliser cp avec un tuyau – trobrock

+1

Cela devrait probablement aller à super utilisateur. La commande que vous cherchez est xargs. Vous pouvez aussi faire quelque chose comme deux tar connectés par un tuyau. –

Répondre

277

Utilisez rsync:

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

Notez que l'utilisation source et source/ sont différents. Une barre oblique signifie copier le contenu du dossier source en destination. Sans la barre oblique, cela signifie copier le dossier source en destination.

Vous pouvez également utiliser --exclude-from=FILE si vous avez beaucoup de répertoires (ou de fichiers) à exclure, FILE étant le nom d'un fichier contenant des fichiers ou des répertoires à exclure.

--exclude peut également contenir des caractères génériques, tels que --exclude=*/.svn*

+33

note 'path1/to/exclude' etc sont un sous-répertoire de "source". – abe

+0

Si vous - comme moi - voulez récursivement copier une structure entière et ne garder qu'un certain type de fichiers alors vous pouvez aussi utiliser rsync: rsync -rav --include = */--include = "*. Txt" - -exclude = * test/mytest Cela prendra tous les fichiers .txt du répertoire source et les copiera dans le répertoire de destination avec la structure intacte. – slott

+5

Je suggère d'ajouter le --dry-run afin de vérifier quels fichiers vont être copiés. – loretoparisi

5

similaires à l'idée de Jeff (non testé):

find . -name * -print0 | grep -v "exclude" | xargs -0 -I {} cp -a {} destination/ 
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 

... Untested

2

vous pouvez utiliser le goudron, avec option --exclude, puis décompressez en destination. par exemple

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

voir la page de manuel de goudron pour plus d'infos

32

Utilisez le goudron le long d'un tuyau.

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

Vous pouvez même utiliser cette technique sur ssh.

+2

Ne devrait-il pas y avoir un point avant le tuyau? C'est à dire, "tar cf - --exclude = dir_to_exclude. |" ... –

+0

Correct, mis à jour. –

+0

@Kyle Butt, Quand je google, il dit que nous utilisons tar pour convertir un groupe de fichiers dans une archive, alors comment cela fonctionnera pour copier le dossier, pouvez-vous expliquer cela, j'essaye ce code, ça fonctionne parfaitement, mais je besoin d'une bonne compréhension dans cette commande. – acer

7

Vous pouvez utiliser find avec l'option -prune.

Un exemple de 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

Des accessoires pour localiser un exemple très pertinent directement à partir d'une page de manuel. –

0

inspiré par @ la réponse de SteveLazaridis, qui échouerait, voici une fonction shell POSIX - il suffit de copier et coller dans un fichier nommé cpx dans yout $PATH et de le rendre executible (chmod a+x cpr). [La source est maintenant maintenue dans mon 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 "[email protected]") 
} 

_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 "[email protected]" 

Exemple d'utilisation

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