fichiers cachés cp avec des motifs glob

13

Situation :

$ mkdir foo && touch foo/.test
$ cp foo/* .
zsh: no matches found: foo/*
(or bash : cp: cannot stat foo/*’: No such file or directory)

J'ai un répertoire plein de dossiers et fichiers cachés. Que se passe-t-il et quelle est la solution?

Pente
la source
dotglobest traduit globdotspar par zsh qui est un nom d'option non valide pour bash .

Réponses:

19

Avertissement: Cette réponse traite spécifiquement de Bash, mais une grande partie s'applique à la question concernant les modèles glob!

Le caractère étoile ( *) est un caractère générique. Il y a un certain ensemble de caractères qu'il remplacera et le premier caractère étant un point ( .) n'en fait pas partie. Ceci est un cas particulier du fait du fonctionnement des systèmes de fichiers Unix, les fichiers commençant par un point sont considérés comme "masqués". Cela signifie que les outils tels que cp, ls, etc. ne « voir » à moins que dit explicitement de le faire.

Exemples

Créons d'abord quelques exemples de données.

$ mkdir .dotdir{1,2} regdir{1,2}
$ touch .dotfile{1,2} regfile{1..3}

Nous avons donc maintenant les éléments suivants:

$ tree -a
.
|-- .dotdir1
|-- .dotdir2
|-- .dotfile1
|-- .dotfile2
|-- regdir1
|-- regdir2
|-- regfile1
|-- regfile2
`-- regfile3

Jouons maintenant à quelques jeux. Vous pouvez utiliser la commande echopour lister ce que *serait un caractère générique ( ) particulier pour une commande donnée comme ceci:

$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3


$ echo reg*
regdir1 regdir2 regfile1 regfile2 regfile3

$ echo .*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2

$ echo .* *
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

$ echo .dotdir*
.dotdir1 .dotdir2

Changer le comportement?

Vous pouvez utiliser la commande shopt -s dotglob pour modifier le comportement du *afin que, en plus des fichiers comme regfile1celui-ci, il corresponde également .dotfile1.

extrait du bash page de manuel

dotglob If set, bash includes filenames beginning with a `.' in the results 
        of pathname expansion.

Exemple:

$ shopt -s dotglob
$ echo *
.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

Vous pouvez annuler ce comportement avec cette commande:

$ shopt -u dotglob
$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3

Ta situation?

Pour vous, vous dites cpque vous souhaitez copier tous les fichiers qui correspondent au modèle *, et il n'y a pas de fichiers.

$ cp foo/.* .

Ou vous pouvez le faire si vous voulez que tout soit dans le foodossier:

$ cp foo .

Ou vous pouvez être explicite:

$ cp foot/.* foo/* .

Une forme plus compacte utilisant l'expansion d'accolade dans bash:

$ cp foo/{.,}* .

À tout moment, vous pouvez utiliser l' echoastuce pour voir quels sont vos modèles de fichiers proposés (c'est le terme de fantaisie pour lequel l'étoile fait partie).

$ echo {.,}*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 abc regdir1 regdir2 regfile1 regfile2 regfile3

Soit dit en passant, si vous allez copier un répertoire de fichiers + d'autres répertoires, vous voulez généralement le faire récursivement, c'est le -Rpassage à cp:

$ cp -R foo/. .
slm
la source
1
Vous voudrez peut-être mentionner l'utilisation de shopt -s dotglob, echo *puis donne:.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3
Drav Sloan
@DravSloan - merci pour la suggestion. Je l'ai ajouté.
slm
cp -r foo .ne fonctionnera généralement pas car cprefusera de se recopier foosur lui-même, vous vouliez probablement dire cp -R foo/. .. (notez que cp -Rc'est le standard).
Stéphane Chazelas
1
Notez également que (très sensiblement), zshn'inclut pas .et ..dans l'expansion de .*. Dans zsh, si un modèle ne correspond pas, la commande est abandonnée (de manière assez sensible à nouveau), donc dans le cas général, cp foo/{,.}* .échouera s'il n'y a pas de fichiers cachés ou non cachés.
Stéphane Chazelas
7
Cette réponse s'applique uniquement à bash. Le demandeur utilise zsh, où la réponse est beaucoup plus simple et shoptn'existe pas.
Gilles 'SO- arrête d'être méchant'
29

Avec zsh, la manière typique est d'utiliser le D qualificatif glob (pour inclure les fichiers [D] ot):

cp foo/*(D) .

Notez qu'avec ou sans D, les globes zsh n'incluent jamais .ni ..(comme vous vous en doutez).

Stéphane Chazelas
la source
4

Le shell traite ces fichiers comme cachés lorsqu'il résout le *caractère, il cpne reçoit donc aucun de ces noms de fichiers comme arguments.

Vous pouvez les copier en spécifiant explicitement cp foo/.* .

rejouer
la source
1

Si ce que vous voulez faire est de copier tous les fichiers et répertoires d'un endroit à un autre, vous pouvez utiliser la rsynccommande standard . Dans l'exemple que vous donnez ci-dessus:

mkdir foo && touch foo/.test
rsync -a foo/ .

copiera récursivement tout le contenu de foo, y compris les fichiers cachés et les répertoires cachés, dans le répertoire courant. La barre oblique de fin à la fin de foo/est importante pour rsync; avec elle, seul le contenu de fooest copié, sans cela rsync copiera fooégalement. Par exemple:-

mkdir src && mkdir dest && touch src/.test
rsync -a src  dest    // copies 'src' contents to 'dest/src'
rsync -a src/ dest    // copies 'src' contents to 'dest' 

Il existe de nombreuses autres options disponibles pour modifier rsync, y compris la copie entre machines.

Rob Swarbrick
la source
1

La réponse zsh de Stéphane Chazelas est correcte pour une seule expression avec un glob. Cependant, si vous souhaitez le définir pour toutes les expressions par défaut, vous pouvez utiliser

setopt GLOB_DOTS

Mettez-le dans votre ~/.zshrcpour le rendre permanent.

Sparhawk
la source