Quel est le paramètre dans bash pour la globalisation, pour contrôler si * correspond aux fichiers dot

12

J'ai été surpris récemment quand j'ai fait quelque chose comme mv ./* ../somedirectory et j'ai constaté que les fichiers comme .gitignoren'étaient pas déplacés.

Je fais la plupart de mon travail en zsh sur OS X, et cette surprise m'a mordu en bash sur CentOS. J'ai essayé bash sur OS X et j'ai trouvé le même comportement: *ne correspond pas aux fichiers dot. Cela me semble très indésirable, mais apparemment c'est le bash par défaut. (C'est peut-être aussi la valeur par défaut de zsh pour tout ce dont je me souviens, mais je l'ai peut-être changé il y a des années dans mon .zshrc et j'ai oublié qu'il fonctionnait différemment.)

Comment puis-je configurer bash pour qu'il se comporte comme prévu: pour que * corresponde à tous les fichiers et n'ignore pas les fichiers dot.

Dans le cas où cela n'est pas clair du tout, voici comment le reproduire

cd /tmp
mkdir {t,d}est
touch test/{.,}{1,2,3,4,5,6,7}
ls -hal test
mv test/* dest
ls -hal test     # notice dot files are still there
ls -hal dest     # notice only some files were mv'ed
iconoclaste
la source
Oui, ils sont liés. Cela ne s'est pas révélé lorsque j'ai cherché (probablement parce que l'auteur de la question n'a pas mentionné les fichiers globbing ou dot), mais c'est vraiment une question différente. Il demandait comment déplacer les fichiers, et je demandais comment changer le comportement du shell, et spécifiquement pour bash. Je n'aurais peut-être pas demandé si cette question était apparue pour moi (car votre réponse extrêmement complète et approfondie comprend le paramètre bash) mais c'est toujours une question différente, et quelqu'un qui cherche une réponse à ma question ne va pas nécessairement trouver sa question.
iconoclaste
Toute cette affaire «quelqu'un qui cherche une réponse à ma question ne va pas nécessairement trouver sa question» est exactement la raison pour laquelle nous avons cette façon de fermer les questions en double.
Gilles 'SO- arrête d'être méchant'
oui, mais vous semblez manquer le point principal, c'est que c'est une question différente .
iconoclaste du

Réponses:

14

Frapper

Comme vous l'avez déjà remarqué, bash ne correspondra pas à un .au début du nom ni à une barre oblique. Pour modifier la correspondance concernant le point, vous devez définir l' dotgloboption - man bash :

dotglob Si défini, bash inclut les noms de fichiers commençant par un «. dans
    les résultats de l'expansion du nom de chemin.

Pour l'activer / le définir avec l'utilisation bash shopt, par exemple:

shopt -s dotglob


Pour zsh, vous pouvez également utiliser l' dotgloboption mais mais vous devrez l'utiliser setoptpour l'activer, par exemple:

setopt dotglob
Ulrich Dangel
la source
2
Avec zsh, la meilleure option est d'activer dotglob par glob:mv test/*(D) /dest
Stéphane Chazelas
7

J'ai testé cela et cela résout le problème:

shopt -s dotglob

Production:

~/stackexchangeanswers/40662$ ls -hal dest
total 8.0K
drwxr-xr-x 2 jodiec jodiec 4.0K 2012-06-12 22:15 .
drwxr-xr-x 4 jodiec jodiec 4.0K 2012-06-12 22:15 ..
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 3
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .3
...snipped....
Jodie C
la source
Désolé mais Ulrich a répondu en premier.
iconoclaste
3
Nous faisons tous partie de la même équipe. C'est bon.
Jodie C
6

*est un glob qui est développé par le shell. Par défaut, les shells n'incluent pas les fichiers dont le nom commence par un .(appelé fichiers cachés ou fichiers dot) sauf si le début .est entré littéralement.

*ou [.]*ou ?*ou *.*ou dir/*n'inclura pas les fichiers dot.

.*ou le dir/.*fera.

Vous pourriez donc faire:

mv -- * .* /dest/

Cependant, certains shells, y compris bash(mais pas zsh, mkshni fish), ont cette erreur que l'expansion d' .*inclure les entrées de répertoire spéciales .et .., que vous ne voulez pas ici (et généralement ne voulez jamais un glob à inclure, c'est pourquoi je l'appelle une erreur).

Pour cette raison, vous constaterez que parfois les gens utilisent (dans des coquilles de type Bourne):

mv -- * .[!.]* ..?* /dest/

Il s'agit de trois globes, le premier correspondant aux fichiers non masqués, le second commençant par .suivi d'un caractère différent de .et le troisième commençant par ..suivi d'au moins un caractère.

Cependant, certains obus modernes ont de meilleurs moyens de contourner cela

zsh

Avec zsh, vous pouvez utiliser le (D)qualificatif glob pour spécifier que le glob doit inclure des fichiers dot:

mv -- *(D) /dest/

zsha également corrigé cette autre erreur du shell Bourne en ce que si le modèle ne correspond pas, la mvcommande n'est pas exécutée.

Comme dit ci-dessus, il n'inclura jamais .non plus ..dans ses globes, donc

mv -- * .* /dest/

sera en sécurité. Cependant, s'il n'y a pas de correspondance de fichier *ou de fichier correspondant, .*la commande sera abandonnée, il serait donc préférable d'utiliser:

mv -- (*|.*) /dest/

Comme dans certains autres shells, vous pouvez également forcer tous les globs à inclure des dotfiles (par exemple si vous vous retrouvez à vouloir des dotfiles inclus le plus souvent) avec:

setopt dotglob

ou:

set -o dotglob

Après cela, si vous souhaitez qu'un glob particulier n'inclue pas de fichiers dot, vous pouvez l'écrire:

echo *(^D)

Ou:

echo [^.]*

Frapper

Malheureusement, il bashn'y a pas de qualificatifs glob. Il vous reste donc à activer l'inclusion de fichiers dot à l'échelle mondiale. Dans bash, la syntaxe est:

shopt -s dotglob

(et utiliser [^.]*pour les globes sans fichiers cachés).

Avec dotglob, bashn'inclut pas .ni ..dans les globes comme *, mais le fait toujours pour les globes comme .*.

Si vous définissez la GLOBIGNOREvariable sur quelque chose de non vide, elle active automatiquement l' dotgloboption et exclut .et ..des .*globs mais pas de dir/.*ou .*/fileceux (!) De sorte que la sauvegarde est assez inutile. Vous pourriez le faire, GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'mais cela briserait des globes comme */.ou ./*ou ../*.

Une meilleure solution consiste à utiliser [.]*ou dir/[.]*ou [.]*/file(avec dotglobactivé) pour développer les fichiers dot à l'exception de .et ...

poisson

fishles globes n'incluent pas .ni ... Lorsqu'il n'y a pas de correspondance, selon la version, cela fonctionnera comme zsh(ou bash -o failglob) ou bash -o nullglob.

mv -- * .* /dest/

Fonctionnerait s'il y avait des fichiers cachés et non cachés. Sinon, YMMV et avec certaines versions, il peut appeler mv -- /dests'il n'y a pas de fichier du tout.

ksh93

Aucun qualificatif global dans les ksh93deux cas. Vous pouvez inclure des fichiers dot dans des globes avec:

FIGNORE='@(.|..)'

Contrairement à bash's GLOBIGNORE, cela se fait correctement et résout également le problème d' .*inclusion de .et ...

yash

yasha une dot-globoption ( set -o dot-glob), mais contrairement à bash, les extensions globales (même de *) incluent .et ..donc c'est assez inutile.

tcsh

set globdot

Fonctionne comme dans bash, c'est-à-dire *inclure les fichiers dot sauf .et ..mais .*inclut toujours .et ..(et vous pouvez utiliser [.]*pour développer les fichiers cachés sauf .et ..).

Stéphane Chazelas
la source
4

La manière la plus simple en bash d'imprimer des fichiers de points correspondants, en ignorant .et ..c'est:

$ GLOBIGNORE=/+/
$ printf '%s\n' *


Tester:

$ cd tmp; mkdir empty; cd empty; touch {,.}{a..h}
$ GLOBIGNORE=/+/
$  ls *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

Il a imprimé tous les fichiers avec des points ou non, à l'exception notable de .et ...

Votre exemple fonctionnera également correctement:

$ cd /tmp; mkdir {t,d}est; touch test/{,.}{a..h}
$ mv test/* dest/
$ ls -a test
.  ..
$ ls -a test/*
ls: cannot access test/*: No such file or directory

Vide de fichiers importants.
Les fichiers système . ..existent toujours mais une extension shell (en utilisant *) ne les inclura pas.

Et le répertoire de destination contient tous les fichiers:

$ cd dest
$ ls -a *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

Pourquoi?

Cela fonctionne car la configuration de GLOBIGNORE a ces effets secondaires:

  • Définissez dotglob ( shopt -s dotglob) sur des fichiers de points mathématiques lors des extensions.
  • Les noms de fichiers . et ..sont ignorés lorsque GLOBIGNORE est défini et non nul.

Détails dans le manuel: LESS=+'/The GLOBIGNORE' man bash.

De plus, nous devons définir la variable GLOBIGNORE pour qu'elle contienne un nom auquel aucun nom de fichier ne pourrait correspondre:
une barre oblique (et autre chose juste à titre de double précaution).

$ GLOBIGNORE=/+/

Il peut être utile de définir également nullglob si cela est nécessaire pour éviter d'obtenir un *lorsqu'il n'y a pas de fichier correspondant au *.


la source
0

Très intéressé par ce paramètre de GLOBIGNORE = / + / @ user79743

D'après mon expérience MHS dotglob est une mauvaise affaire à presque toutes vos commandes (cp, mv , etc.) , mais la même chose est vraie mise la nullglob ( en particulier avec des expressions régulières qui ont besoin alors s'échapper!).

Néanmoins, si vous devez les définir au début de votre programme mais interférer dans des parties spécifiques où vous appelez des commandes comme cp, mv, etc. ou utilisez des expressions régulières, procédez comme suit:

  # set dotglob, unset nullglob AFTER this
  is_nullglob=$( shopt -s | egrep -i '.*nullglob' )
  is_dotglob=$( shopt -s | egrep -i '.*dotglob' )
  [[ $is_nullglob ]] && shopt -u nullglob
  [[ ! $is_dotglob ]] && shopt -s dotglob
  # ... call commands ...
  # .....................
  # reset dotglob, nullglob to their previous values
  [[ $is_nullglob ]] && shopt -s nullglob
  [[ ! $is_dotglob ]] && shopt -u dotglob
centurien
la source