Qual è l'impostazione in bash per globbing, per controllare se * corrisponde ai file dot


12

Sono stato sorpreso di recente quando ho fatto qualcosa di simile mv ./* ../somedirectory e ho scoperto che i file come .gitignorenon sono stati spostati.

Faccio la maggior parte del mio lavoro in zsh su OS X, e questa sorpresa mi ha colpito bash su CentOS. Ho provato bash su OS X e ho riscontrato lo stesso comportamento: *non corrisponde ai file dot. Questo mi sembra molto indesiderabile, ma a quanto pare è l'impostazione predefinita bash. (Potrebbe essere anche il predefinito zsh per tutto ciò che ricordo, ma potrei averlo cambiato anni fa nel mio .zshrc e dimenticato che abbia mai funzionato diversamente.)

Come posso configurare bash in modo che si comporti come mi aspettavo: affinché * corrispondesse a tutti i file e non ignorasse i file punto.

Nel caso in cui ciò non sia affatto chiaro, ecco come riprodurlo

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


Sì, sono correlati. Non è stato visualizzato quando ho cercato (probabilmente perché l'interrogatore non ha menzionato i file globbing o dot), ma è davvero una domanda diversa. Stava chiedendo come spostare i file e io chiedevo come cambiare il comportamento della shell, e in particolare per bash. Non avrei potuto chiedermi se quella domanda mi fosse apparsa (dal momento che la tua risposta estremamente completa e approfondita include l'impostazione di bash) ma è ancora una domanda diversa, e qualcuno che cerca una risposta alla mia domanda non necessariamente troverà la sua domanda.
iconoclasta,

L'intera attività di "qualcuno alla ricerca di una risposta alla mia domanda non trova necessariamente la sua domanda" è esattamente il motivo per cui abbiamo questo modo di chiudere le domande come duplicati.
Gilles 'SO- smetti di essere malvagio' il

sì, ma sembra che manchi il punto principale, ovvero che è una domanda diversa .
iconoclasta,

Risposte:


14

bash

Come hai già notato, bash non corrisponderà .a all'inizio del nome o a una barra. Per cambiare la corrispondenza relativa al punto devi impostare l' dotglobopzione - man bash :

dotglob Se impostato, bash include i nomi di file che iniziano con un `. ' in
    i risultati dell'espansione del nome percorso.

Per abilitarlo / impostarlo con bash shopt, ad esempio:

shopt -s dotglob


Per zsh puoi anche usare l' dotglobopzione ma dovrai usarla setoptper abilitarla, ad esempio:

setopt dotglob

2
Con zsh, l'opzione migliore è abilitare dotglob su base globale :mv test/*(D) /dest
Stéphane Chazelas,

7

Ho provato questo e risolve il problema:

shopt -s dotglob

Produzione:

~/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....

Scusa ma Ulrich ha risposto per primo.
iconoclasta,

3
Siamo tutti nella stessa squadra. Va bene.
Jodie C,

6

*è un glob che viene espanso dalla shell. Per impostazione predefinita, le shell non includono file il cui nome inizia con un .(chiamato file nascosti o dotfile) a meno che il lead non .sia inserito letteralmente.

*o [.]*o ?*o *.*o dir/*non includerà dotfile.

.*o dir/.*volontà.

Quindi potresti fare:

mv -- * .* /dest/

tuttavia alcune shell tra cui bash(ma non zsh, mkshfish) hanno quella cattiva funzionalità che l'espansione di .*include le voci di directory speciali .e .., che non si desidera qui (e generalmente non si desidera che un glob includa, motivo per cui lo chiamo malfunzionamento).

Per questo motivo, scoprirai che a volte le persone usano (nelle shell tipo Bourne):

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

Sono tre globs, il primo corrispondente a file non nascosti, il secondo nome file che inizia con .seguito da un carattere diverso da .e il terzo nome file che inizia con ..seguito da almeno un carattere.

Tuttavia alcune conchiglie moderne hanno modi migliori per aggirare questo

zsh

Con zsh, puoi usare il (D)qualificatore glob per specificare che il glob dovrebbe includere dotfile:

mv -- *(D) /dest/

zshrisolto anche quell'altro malfunzionamento della shell Bourne in quanto se il modello non corrispondeva, il mvcomando non veniva eseguito.

Come detto sopra, non includerà mai ...nei suoi globs, quindi

mv -- * .* /dest/

sarà al sicuro. Tuttavia, se non è presente alcuna corrispondenza file *o corrispondenza file, .*il comando verrà interrotto, quindi sarebbe meglio usare:

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

Come in alcune altre shell, puoi anche forzare tutti i globs a includere dotfile (ad esempio se ti ritrovi a desiderare dotfile inclusi più spesso) con:

setopt dotglob

o:

set -o dotglob

Dopodiché, se vuoi che un particolare glob non includa dotfile, puoi scriverlo:

echo *(^D)

O:

echo [^.]*

bash

Sfortunatamente bashnon ha le qualificazioni glob. Quindi ti rimane con l'abilitazione dell'inclusione dotfile a livello globale. In bash, la sintassi è:

shopt -s dotglob

(e utilizzare [^.]*per globs senza file nascosti).

Con dotglob, bashnon include ...in globs like *, ma lo fa ancora per globs like .*.

Se imposti la GLOBIGNOREvariabile su qualcosa di non vuoto, abilita automaticamente l' dotglobopzione ed esclude .e ..dai .*glob ma non da dir/.*o .*/filequelli (!) In modo che la protezione sia piuttosto inutile. Si potrebbe fare, GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'ma poi si spezzerebbe globs come */.o ./*o ../*.

Una soluzione migliore consiste nell'utilizzare [.]*o dir/[.]*o [.]*/file(con dotglobabilitato) per espandere dotfile tranne .e ...

pesce

fishglobs non includono .... Quando non c'è corrispondenza, a seconda della versione, funzionerà come zsh(o bash -o failglob) o bash -o nullglob.

mv -- * .* /dest/

Funzionerebbe se ci sono sia file nascosti che non nascosti. Altrimenti, YMMV e con alcune versioni, potrebbe chiamare mv -- /destse non ci sono file.

ksh93

Nessun qualificatore glob in ksh93neanche. Puoi includere dotfile in globs con:

FIGNORE='@(.|..)'

Contrariamente a bashs' GLOBIGNORE, che è fatto correttamente e anche le correzioni al problema della .*inclusi .e ...

Yash

yashha dot-globun'opzione ( set -o dot-glob), ma al contrario bash, le espansioni glob (anche di *) includono .e ..quindi è piuttosto inutile.

tcsh

set globdot

Funziona come in bash, ovvero *include file dot tranne .e ..ma .*include ancora .e ..(e puoi usare [.]*per espandere file nascosti tranne .e ..).


4

Il modo più semplice in bash per stampare file dot corrispondenti, ignorando .ed ..è:

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


Testare:

$ 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

Ha stampato tutti i file con punti o no, con la notevole eccezione di .e ...

Il tuo esempio funzionerà anche correttamente:

$ 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

Vuoto di file che contano.
I file di sistema . ..esistono ancora ma un'espansione della shell (usando *) non li includerà.

E la dest directory inazione contiene tutti i file:

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

Perché?

Questo funziona perché l'impostazione di GLOBIGNORE ha questi effetti collaterali:

  • Imposta dotglob ( shopt -s dotglob) su file di punti matematici su espansioni.
  • I nomi dei file . e ..vengono ignorati quando GLOBIGNORE è impostato e non nullo.

Dettagli nel manuale: LESS=+'/The GLOBIGNORE' man bash.

Inoltre, dobbiamo impostare la variabile GLOBIGNORE in modo che contenga un nome che nessun nome di file può corrispondere:
una barra (e qualcos'altro come doppia precauzione).

$ GLOBIGNORE=/+/

Può essere utile anche impostare nullglob se è necessario per evitare di ottenere un *quando non ci sono file corrispondenti al *.


0

Molto interessata questa impostazione di GLOBIGNORE = / + / @ user79743

Dalla mia esperienza disinserimento dotglob è un cattivo affare per quasi tutti i comandi (cp, mv ecc), ma lo stesso vale per l'impostazione del nullglob (in particolare con le espressioni regolari che quindi hanno bisogno di fuga!).

Tuttavia, se è necessario impostarli all'inizio del programma, ma interferire in parti specifiche in cui si chiamano comandi come cp, mv ecc. O si usano espressioni regolari, basta fare questo:

  # 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
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.