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

kes 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. (Yevalcomienza 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
$HOMEo 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
$excludeno 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 -pathsi 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
$HOMElugar de hacerlo~por la misma razón que antes, y$dirsdebe estar sin comillas en lafindlínea de comando para que se divida, pero$patterndebe citarse para que el shell no lo expanda accidentalmente.(Creo que podría jugar con
-maxdepthGNU 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á
evalalgunas de las cadenasfuente
$excludecontiene los comodines, necesitaría deshabilitar el globbing antes de usar el operador split + glob en él y restaurarlo para$i/$jy no usarevalsino usar"$i"/$jLa respuesta de @ilkkachu resolvió el principal problema global. Todo el crédito para él.
V1
Sin embargo, debido a que
excludecontienen 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/$jconvierte 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/$jsolo devuelve la cadena original.V2
V2 utiliza comillas simples
evalyshopt -s nullglobpara obtener un resultado limpio. No se requiere verificación final de archivo / directorio.fuente
for j in $excludeel interior, los globos$excludepodrían expandirse en el momento de esa$excludeexpansión (y recurrirevala 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 -fantes del último y unset +fpara 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$ldebe citarse allí.excludeydirsestán entre comillas simples (), so no globbing tilleval`.foo=*Yfoo='*'es lo mismo. Peroecho $fooyecho "$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}stringes expandir como$array[1]string $array[2]string....$=varconsiste en dividir las palabras en la variable (¡algo que hacen otros shells de forma predeterminada!),$~varse 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$farriba en otras conchas)).(N)es un calificador de glob que activa nullglob para cada uno de esos globos que resultan de esa$^array1/$^array2expansió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.dben uno, lo que significa que si ese particular no existe, no está incluido.fuente
excludeencerrada está afectando la salida.$^arraydebe 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.