Información del sistema
OS: OS X
bash: GNU bash, versión 3.2.57 (1) -release (x86_64-apple-darwin16)
Antecedentes
Quiero que Time Machine excluya un conjunto de directorios y archivos de todo mi proyecto git / nodejs. Mis directorios de proyecto están en el directorio ~/code/private/
y ~/code/public/
por eso estoy tratando de usar bash looping para hacer el tmutil
.
Problema
Version corta
Si tengo una variable de cadena calculadak
, ¿cómo hago que sea global o justo antes de un ciclo for:
i='~/code/public/*'
j='*.launch'
k=$i/$j # $k='~/code/public/*/*.launch'
for i in $k # I need $k to glob here
do
echo $i
done
En la versión larga a continuación, verá k=$i/$j
. Entonces no puedo codificar la cadena en el bucle for.
Versión larga
#!/bin/bash
exclude='
*.launch
.classpath
.sass-cache
Thumbs.db
bower_components
build
connect.lock
coverage
dist
e2e/*.js
e2e/*.map
libpeerconnection.log
node_modules
npm-debug.log
testem.log
tmp
typings
'
dirs='
~/code/private/*
~/code/public/*
'
for i in $dirs
do
for j in $exclude
do
k=$i/$j # It is correct up to this line
for l in $k # I need it glob here
do
echo $l
# Command I want to execute
# tmutil addexclusion $l
done
done
done
Salida
No son globbed. No es lo que quiero.
~/code/private/*/*.launch
~/code/private/*/.DS_Store
~/code/private/*/.classpath
~/code/private/*/.sass-cache
~/code/private/*/.settings
~/code/private/*/Thumbs.db
~/code/private/*/bower_components
~/code/private/*/build
~/code/private/*/connect.lock
~/code/private/*/coverage
~/code/private/*/dist
~/code/private/*/e2e/*.js
~/code/private/*/e2e/*.map
~/code/private/*/libpeerconnection.log
~/code/private/*/node_modules
~/code/private/*/npm-debug.log
~/code/private/*/testem.log
~/code/private/*/tmp
~/code/private/*/typings
~/code/public/*/*.launch
~/code/public/*/.DS_Store
~/code/public/*/.classpath
~/code/public/*/.sass-cache
~/code/public/*/.settings
~/code/public/*/Thumbs.db
~/code/public/*/bower_components
~/code/public/*/build
~/code/public/*/connect.lock
~/code/public/*/coverage
~/code/public/*/dist
~/code/public/*/e2e/*.js
~/code/public/*/e2e/*.map
~/code/public/*/libpeerconnection.log
~/code/public/*/node_modules
~/code/public/*/npm-debug.log
~/code/public/*/testem.log
~/code/public/*/tmp
~/code/public/*/typings
bash
shell-script
quoting
wildcards
John Siu
fuente
fuente
k
es una cadena calculada, y necesito que permanezca así hasta el bucle. Por favor revise mi versión larga.Respuestas:
Puede forzar otra ronda de evaluación con
eval
, pero eso no es realmente necesario. (Yeval
comienza a tener serios problemas en el momento en que los nombres de sus archivos contienen caracteres especiales como$
). El problema no es el engorde, sino la expansión de tilde.El globalizado ocurre después de la expansión de la variable, si la variable no se cita, como aquí (*) :
Entonces, en la misma línea, esto es similar a lo que hiciste, y funciona:
Pero con la tilde no lo hace:
Esto está claramente documentado para Bash:
La expansión de tildes ocurre antes de la expansión de variables, por lo que las tildes dentro de las variables no se expanden. La solución fácil es usar
$HOME
o la ruta completa en su lugar.(* expandir globos desde variables generalmente no es lo que quieres)
Otra cosa:
Cuando recorres los patrones, como aquí:
Tenga en cuenta que, como
$exclude
no se cita, está dividido y también englobado en este punto. Entonces, si el directorio actual contiene algo que coincide con el patrón, se expande a eso:Para evitar esto, use una variable de matriz en lugar de una cadena dividida:
Como una ventaja adicional, las entradas de matriz también pueden contener espacios en blanco sin problemas con la división.
Se podría hacer algo similar
find -path
si no le importa en qué nivel de directorio deberían estar los archivos de destino. Por ejemplo, para encontrar cualquier ruta que termine en/e2e/*.js
:Tenemos que usarlo en
$HOME
lugar de hacerlo~
por la misma razón que antes, y$dirs
debe estar sin comillas en lafind
línea de comando para que se divida, pero$pattern
debe citarse para que el shell no lo expanda accidentalmente.(Creo que podría jugar con
-maxdepth
GNU para limitar la profundidad de la búsqueda, si le importa, pero ese es un problema un poco diferente).fuente
find
? De hecho, también estoy explorando esa ruta, ya que el ciclo for se está complicando. Pero estoy teniendo dificultades con el 'camino'."${array[@]}"
(¡con las comillas!) está documentado (ver aquí y aquí ) para expandir a los elementos como palabras distintas sin dividirlos más.[abc]
es una parte estándar de los patrones globales , como?
, no creo que sea necesario cubrirlos todos aquí.Puede guardarlo como una matriz en lugar de una cadena para usarlo más tarde en muchos casos y dejar que ocurra el bloqueo cuando lo defina. En su caso, por ejemplo:
o en el ejemplo posterior, necesitará
eval
algunas de las cadenasfuente
$exclude
contiene los comodines, necesitaría deshabilitar el globbing antes de usar el operador split + glob en él y restaurarlo para$i/$j
y no usareval
sino usar"$i"/$j
La respuesta de @ilkkachu resolvió el principal problema global. Todo el crédito para él.
V1
Sin embargo, debido a que
exclude
contienen entradas con y sin comodín (*), y también pueden no existir en absoluto, se necesita una comprobación adicional después de la aplicación global$i/$j
. Estoy compartiendo mis hallazgos aquí.Explicación de salida
A continuación se muestra el resultado parcial para explicar la situación.
Lo anterior se explica por sí mismo.
Lo anterior se muestra porque la entrada de exclusión (
$j
) no tiene comodín, se$i/$j
convierte en una concatenación de cadena simple. Sin embargo, el archivo / directorio no existe.Lo anterior se muestra como una entrada exclude (
$j
) que contiene comodines pero no tiene coincidencia de archivo / directorio, el globing$i/$j
solo devuelve la cadena original.V2
V2 utiliza comillas simples
eval
yshopt -s nullglob
para obtener un resultado limpio. No se requiere verificación final de archivo / directorio.fuente
for j in $exclude
el interior, los globos$exclude
podrían expandirse en el momento de esa$exclude
expansión (y recurrireval
a eso es pedir problemas). Desea habilitar el globbing parafor i in $dir
, yfor l in $k
, pero no parafor j in $exclude
. Querrías unset -f
antes del último y unset +f
para el otro. En términos más generales, querrá ajustar su operador de división + glob antes de usarlo. En cualquier caso, no desea dividir + glob paraecho $l
, por lo que$l
debe citarse allí.exclude
ydirs
están entre comillas simples (), so no globbing till
eval`.foo=*
Yfoo='*'
es lo mismo. Peroecho $foo
yecho "$foo"
no lo son (en shells comobash
, se ha corregido en shells como zsh, fish o rc, vea también el enlace de arriba). Aquí no desea utilizar ese operador, pero en algunos lugares sólo la parte dividida, y en otros sólo la parte pegote.Con
zsh
:${^array}string
es expandir como$array[1]string $array[2]string...
.$=var
consiste en dividir las palabras en la variable (¡algo que hacen otros shells de forma predeterminada!),$~var
se pega a la variable (algo que otros shells también de forma predeterminada (cuando generalmente no quiere que lo hagan, habría tenido que citar$f
arriba en otras conchas)).(N)
es un calificador de glob que activa nullglob para cada uno de esos globos que resultan de esa$^array1/$^array2
expansión. Eso hace que los globos se expandan a la nada cuando no coinciden. Eso también pasa a convertir un no-glob~/code/private/foo/Thumbs.db
en uno, lo que significa que si ese particular no existe, no está incluido.fuente
exclude
encerrada está afectando la salida.$^array
debe hacerse en dos pasos separados para asegurarse de que los elementos vacíos se descartan (ver edición). Parece un errorzsh
, plantearé el problema en su lista de correo.