Quiero usarlo find
para buscar archivos en un conjunto de carpetas restringidas por comodines, pero donde hay espacios en el nombre de la ruta.
Desde la línea de comando, esto es fácil. Todos los siguientes ejemplos funcionan.
find te*/my\ files/more -print
find te*/'my files'/more -print
find te*/my' 'files/more -print
Estos encontrarán archivos en, por ejemplo, terminal/my files/more
y tepid/my files/more
.
Sin embargo, necesito que esto sea parte de un guión; lo que necesito es algo como esto:
SEARCH='te*/my\ files/more'
find ${SEARCH} -print
Desafortunadamente, haga lo que haga, no parece poder mezclar comodines y espacios en un find
comando dentro de un script. El ejemplo anterior devuelve los siguientes errores (tenga en cuenta la duplicación inesperada de la barra invertida):
find: ‘te*/my\\’: No such file or directory
find: ‘files/more’: No such file or directory
Intentar utilizar comillas también falla.
SEARCH="te*/'my files'/more"
find ${SEARCH} -print
Esto devuelve los siguientes errores, habiendo ignorado el significado de las comillas:
find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory
Aquí hay un ejemplo más.
SEARCH='te*/my files/more'
find ${SEARCH} -print
Como se esperaba:
find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory
Cada variación que he probado devuelve un error.
Tengo una solución alternativa que es potencialmente peligrosa porque devuelve demasiadas carpetas. Convierto todos los espacios en un signo de interrogación (comodín de un solo carácter) como este:
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?} # Convert every space to a question mark.
find ${SEARCH} -print
Este es el equivalente a:
find te*/my?files/more -print
Esto devuelve no sólo las carpetas correctas sino también los archivos terse/myxfiles/more
, lo que no se supone que haga.
¿Cómo puedo lograr lo que estoy tratando de hacer? Google no me ha ayudado :(
Respuesta1
Exactamente el mismo comando debería funcionar bien en un script:
#!/usr/bin/env bash
find te*/my\ files/ -print
Si ustednecesidadal tenerlo como variable, se vuelve un poco más complejo:
#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print
ADVERTENCIA:
Usarlo eval
de esa manera no es seguro y puede resultar en la ejecución de código arbitrario y posiblemente dañino si los nombres de sus archivos pueden contener ciertos caracteres. VerPreguntas frecuentes sobre bash 48para detalles.
Es mejor pasar la ruta como argumento:
#!/usr/bin/env bash
find "$@" -name "file*"
Otro enfoque es evitarlo find
por completo y utilizar las funciones globbing y globbing extendidas de bash:
#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done
La globstar
opción bash te permite hacer **
coincidencias de forma recursiva:
globstar
If set, the pattern ** used in a pathname expansion con‐
text will match all files and zero or more directories
and subdirectories. If the pattern is followed by a /,
only directories and subdirectories match.
Para que funcione al 100% como buscar e incluir archivos de puntos (archivos ocultos), utilice
#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done
Puedes igualarlos echo
directamente sin el bucle:
echo te*/my\ files/**
Respuesta2
¿Qué tal las matrices?
$ tree Desktop/ Documents/
Desktop/
└── my folder
└── more
└── file
Documents/
└── my folder
├── folder
└── more
5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}"
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder
(*)
se expande en una matriz de lo que coincida con el comodín. Y "${SEARCH[@]}"
se expande a todos los elementos de la matriz ( [@]
), cada uno de ellos citado individualmente.
Tardíamente me doy cuenta de que el propio Find debería ser capaz de hacer esto. Algo como:
find . -path 'D*/my folder/more/'
Respuesta3
Está un poco anticuado ahora pero, si eso puede ayudar a alguien con esta pregunta, usar el símbolo de clasificación de RE [[.space.]]
sin comillas $SEARCH
variables dentro de los argumentos de la línea de comando de búsqueda funciona siempre y cuando no haya caracteres especiales en los nombres de ruta expandidos en lugar del asterisco.
set -x
mkdir -p te{flon,nnis,rrine}/my\ files/more
SEARCH=te*/my[[.space.]]files/more
find $SEARCH
dar los siguientes resultados:
+ mkdir -p 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
+ SEARCH='te*/my[[.space.]]files/more'
+ find 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
teflon/my files/more
tennis/my files/more
terrine/my files/more
Para evitar la aparición de caracteres no deseados, se puede reemplazar el asterisco ( *
) por cualquier elemento de clasificación (caracteres):
set -x
mkdir -p -- te{,-,_}{flon,nnis,rrine}/my\ files/more
SEARCH=te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more
find $SEARCH
dando los siguientes resultados:
+ mkdir -p -- 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more' \
'te-flon/my files/more' 'te-nnis/my files/more' 'te-rrine/my files/more' \
'te_flon/my files/more' 'te_nnis/my files/more' 'te_rrine/my files/more'
+ SEARCH='te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more'
+ find 'te-flon/my files/more' 'te_flon/my files/more' 'teflon/my files/more' \
'te-nnis/my files/more' 'te_nnis/my files/more' 'tennis/my files/more' \
'te-rrine/my files/more' 'te_rrine/my files/more' 'terrine/my files/more'
te-flon/my files/more
te_flon/my files/more
teflon/my files/more
te-nnis/my files/more
te_nnis/my files/more
tennis/my files/more
te-rrine/my files/more
te_rrine/my files/more
terrine/my files/more
Tenga en cuenta que para acortar la línea,
[.hyphen.]
se puede reemplazar por [.-.]
o -
y
[.underscore.]
se puede reemplazar por [._.]
o _
.
Truncar líneas con barras invertidas ( \
) agregadas por mí para facilitar la lectura.
Respuesta4
Finalmente descubrí la respuesta.
Agregue una barra invertida a todos los espacios:
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }
En este punto, SEARCH
contiene te*/my\ files/more
.
Luego, usa eval
.
eval find ${SEARCH} -print
¡Es así de simple! El uso eval
omite la interpretación que ${SEARCH}
proviene de una variable.