¿Cuál es la configuración en bash para globbing, para controlar si * coincide con los archivos de puntos

12

Me sorprendió recientemente cuando hice algo así mv ./* ../somedirectory y descubrí que los archivos como .gitignoreno se movían.

Hago la mayor parte de mi trabajo en zsh en OS X, y esta sorpresa me mordió en bash en CentOS. Intenté bash en OS X y encontré el mismo comportamiento: *no coincide con los archivos de puntos. Esto me parece muy indeseable, pero aparentemente es el bash predeterminado. (Puede que también sea el valor predeterminado de zsh para todo lo que recuerdo, pero puede que lo haya cambiado hace años en mi .zshrc y olvidado que alguna vez funcionó de manera diferente).

¿Cómo puedo configurar bash para que se comporte como esperaba ?: * para que coincida con todos los archivos y no ignore los archivos de puntos.

En caso de que esto no esté del todo claro, he aquí cómo reproducirlo

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
iconoclasta
fuente
Sí, están relacionados. Eso no apareció cuando busqué (probablemente porque el interrogador no mencionó archivos de puntos o de globo), pero en realidad es una pregunta diferente. Me preguntaba cómo mover los archivos, y yo preguntaba cómo cambiar el comportamiento del shell, y específicamente para bash. Es posible que no haya preguntado si esa pregunta se me apareció (dado que su respuesta extremadamente completa y exhaustiva incluye la configuración bash), pero sigue siendo una pregunta diferente, y alguien que busque una respuesta a mi pregunta no necesariamente va a encontrar su pregunta
iconoclasta
Todo ese asunto de "alguien que busca una respuesta a mi pregunta no necesariamente va a encontrar su pregunta" es exactamente por qué tenemos esta forma de cerrar las preguntas como duplicados.
Gilles 'SO- deja de ser malvado'
Sí, pero parece que te estás perdiendo el punto principal, que es que es una pregunta diferente .
iconoclasta

Respuestas:

14

Golpetazo

Como ya notó, bash no coincidirá con un .al comienzo del nombre o una barra inclinada. Para cambiar la coincidencia con respecto al punto, debe configurar la dotglobopción - man bash :

dotglob Si está configurado, bash incluye nombres de archivo que comienzan con un '.' en
    Los resultados de la expansión del nombre de ruta.

Para habilitarlo / configurarlo con bash use shopt, por ejemplo:

shopt -s dotglob


Para zsh también puede usar la dotglobopción, pero deberá usarla setoptpara habilitarla, por ejemplo:

setopt dotglob
Ulrich Dangel
fuente
2
Con zsh, la mejor opción es habilitar dotglob por globo:mv test/*(D) /dest
Stéphane Chazelas
7

Probé esto y resuelve el problema:

shopt -s dotglob

Salida:

~/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
fuente
Lo siento, pero Ulrich respondió primero.
iconoclasta
3
Todos estamos en el mismo equipo. Está bien.
Jodie C
6

*es un globo que se expande por el caparazón. De manera predeterminada, los shells no incluyen archivos cuyo nombre comience con un .(llamados archivos ocultos o archivos de puntos) a menos que el encabezado .se ingrese literalmente.

*o [.]*o ?*o *.*o dir/*no incluirán archivos de puntos.

.*o lo dir/.*haré.

Entonces podrías hacer:

mv -- * .* /dest/

sin embargo, algunos shells que incluyen bash(pero no zsh, mkshni fish) tienen la característica errónea de que la expansión de .*incluir las entradas de directorio especiales .y .., que no desea aquí (y generalmente nunca quiere que se incluya un globo, es por eso que lo llamo una característica incorrecta).

Por esa razón, encontrará que a veces las personas usan (en conchas tipo Bourne):

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

Esos son tres globos, el primero coincide con archivos no ocultos, el segundo nombre de archivo comienza con .seguido de un carácter diferente .y el tercero nombres de archivo comienzan con ..seguido de al menos un carácter.

Sin embargo, algunos proyectiles modernos tienen mejores formas de evitar eso

zsh

Con zsh, puede usar el (D)calificador glob para especificar que el glob debe incluir archivos de puntos:

mv -- *(D) /dest/

zshTambién se corrigió esa otra falla del shell Bourne en que si el patrón no coincide, el mvcomando no se ejecuta.

Como se dijo anteriormente, nunca incluirá .ni ..en sus globos, por lo que

mv -- * .* /dest/

será segura. Sin embargo, si no hay coincidencia de archivos *o no coincide .*el archivo, el comando se cancelará, por lo que sería mejor usar:

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

Al igual que en otros shells, también puede obligar a todos los globos a incluir archivos de puntos (por ejemplo, si desea que los archivos de puntos se incluyan con mayor frecuencia) con:

setopt dotglob

o:

set -o dotglob

Después de eso, si desea que un globo en particular no incluya archivos de puntos, puede escribirlo:

echo *(^D)

O:

echo [^.]*

Golpetazo

Lamentablemente bashno tiene calificadores globales. Por lo tanto, le queda habilitar la inclusión de archivos de puntos a nivel mundial. En bash, la sintaxis es:

shopt -s dotglob

(y usar [^.]*para globos sin archivos ocultos).

Con dotglob, bashno incluye .ni ..en globos como *, pero todavía lo hace para globos como .*.

Si establece la GLOBIGNOREvariable en algo que no esté vacío, entonces habilita automáticamente la dotglobopción y excluye .y ..de los .*globos pero no de dir/.*o .*/fileunos (!) Para que la protección sea bastante inútil. Podrías hacerlo, GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'pero luego rompería globos como */.o ./*o ../*.

Una mejor en torno al trabajo es utilizar [.]*o dir/[.]*o [.]*/file(con dotglobactivado) para ampliar dotfiles excepción .y ...

pez

fishlos globos no incluyen .ni ... Cuando no hay coincidencia, dependiendo de la versión, funcionará como zsh(o bash -o failglob) o bash -o nullglob.

mv -- * .* /dest/

Funcionaría si hay archivos ocultos y no ocultos. De lo contrario, YMMV y con algunas versiones, puede llamar mv -- /destsi no hay ningún archivo.

ksh93

No hay calificador global en ksh93ninguno de los dos. Puede incluir archivos de puntos en globos con:

FIGNORE='@(.|..)'

Al contrario de bashs' GLOBIGNORE, que se ha hecho correctamente y también corrige el problema de .*que incluye .y ...

yash

yashtiene una dot-globopción ( set -o dot-glob), pero al contrario bash, las expansiones globales (incluso de *) incluyen .y, ..por lo tanto, es bastante inútil.

tcsh

set globdot

Funciona como en bash, es decir, *incluye archivos de puntos excepto .y ..pero .*aún incluye .y ..(y puede usar [.]*para expandir archivos ocultos excepto .y ..).

Stéphane Chazelas
fuente
4

La forma más fácil en bash para imprimir archivos de puntos coincidentes, ignorando .y ..es:

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


Probar:

$ 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

Imprimió todos los archivos con puntos o no, con la notable excepción de .y ...

Su ejemplo también funcionará correctamente:

$ 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

Vacío de archivos que importan.
Los archivos del sistema . ..todavía existen, pero una expansión de shell (usando *) no los incluirá.

Y el dest directorio minación contiene todos los archivos:

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

¿Por qué?

Esto funciona porque el establecimiento GLOBIGNORE tiene esta secundarios efectos:

  • Establezca dotglob ( shopt -s dotglob) en archivos de puntos matemáticos en expansiones.
  • Los nombres de archivo . y ..se ignoran cuando GLOBIGNORE está configurado y no es nulo.

Los detalles en el manual: LESS=+'/The GLOBIGNORE' man bash.

Además, debemos configurar la variable GLOBIGNORE para que contenga un nombre que ningún nombre de archivo pueda coincidir:
una barra oblicua (y algo más como una doble precaución).

$ GLOBIGNORE=/+/

Puede ser útil configurar también nullglob si es necesario para evitar obtener un *cuando no hay un archivo que coincida con el *.


fuente
0

Muy interesado en esta configuración de GLOBIGNORE = / + / @ user79743

Desde mi experiencia, desestabilizar dotglob es un mal negocio para casi todos sus comandos (cp, mv, etc.) PERO lo mismo es cierto establecer el nullglob (¡especialmente con expresiones regulares que luego necesitan escapar!).

Sin embargo, si necesita establecerlos al comienzo de su programa pero interferir en partes específicas donde llama a comandos como cp, mv, etc. o usa expresiones regulares, simplemente haga esto:

  # 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
centurias
fuente