¿Cómo pasar el comodín '*' al parámetro de ruta del comando de búsqueda a través de una variable en el script?

¿Cómo pasar el comodín '*' al parámetro de ruta del comando de búsqueda a través de una variable en el script?

Quiero usarlo findpara 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/morey 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 findcomando 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 evalde 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 findpor 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 globstaropció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 echodirectamente 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 $SEARCHvariables 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, SEARCHcontiene te*/my\ files/more.

Luego, usa eval.

eval find ${SEARCH} -print

¡Es así de simple! El uso evalomite la interpretación que ${SEARCH}proviene de una variable.

información relacionada