Si quiero verificar la existencia de un solo archivo, puedo probarlo usando test -e filename
o [ -e filename ]
.
Supongamos que tengo un glob y quiero saber si existen archivos cuyos nombres coincidan con el glob. El globo puede coincidir con 0 archivos (en cuyo caso no necesito hacer nada), o puede coincidir con 1 o más archivos (en cuyo caso necesito hacer algo). ¿Cómo puedo probar si un globo tiene alguna coincidencia? (No me importa cuántas coincidencias hay, y sería mejor si pudiera hacer esto con una if
declaración y sin bucles (simplemente porque me parece más legible).
( test -e glob*
falla si el globo coincide con más de un archivo).
Respuestas:
Escápese del patrón o se expandirá previamente en coincidencias.
El estado de salida es:
stdout
es una lista de archivos que coinciden con el glob .Creo que esta es la mejor opción en términos de concisión y minimizar los posibles efectos secundarios.
ACTUALIZACIÓN : Ejemplo de uso solicitado.
fuente
compgen
es un comando incorporado específico de bash y que no forma parte de los comandos integrados estándar de POSIX Unix shell. pubs.opengroup.org/onlinepubs/9699919799 pubs.opengroup.org/onlinepubs/9699919799/utilities/… Por lo tanto, evite usarlo en scripts donde la portabilidad a otros shells es una preocupación.if ls /tmp/*Files 2>&1 >/dev/null; then echo exists; fi
¿quizás útil para el golf de código? Falla si hay un archivo con el mismo nombre que el glob, que el glob no debería haber coincidido, pero si ese es el caso, probablemente tenga mayores problemas.if ls /tmp/*Files &> /dev/null; then echo exists; fi
compgen
, verman bash
o conhelp compgen
La opción de shell nullglob es de hecho un bashism.
Para evitar la necesidad de guardar y restaurar tediosamente el estado de globo nulo, solo lo configuro dentro de la subshell que expande el globo:
Para una mejor portabilidad y un globbing más flexible, use find:
Las acciones explícitas -print -quit se utilizan para buscar en lugar de la acción implícita -print implícita , de modo que find se cerrará tan pronto como encuentre el primer archivo que coincida con los criterios de búsqueda. Cuando coinciden muchos archivos, esto debería ejecutarse mucho más rápido que
echo glob*
ols glob*
y también evita la posibilidad de sobrecargar la línea de comando expandida (algunos shells tienen un límite de longitud 4K).Si find se siente como una exageración y el número de archivos que probablemente coincidan es pequeño, use stat:
fuente
find
Parece ser exactamente correcto. No tiene casos de esquina, ya que el shell no se está expandiendo (y no pasa un globo expandido a otro comando), es portátil entre los shells (aunque aparentemente no todas las opciones que usa están especificadas por POSIX), y es más rápido quels -d glob*
(la respuesta aceptada anterior) porque se detiene cuando llega al primer partido.shopt -u failglob
ya que estas opciones parecen entrar en conflicto de alguna manera.find
solución también coincidirá con un nombre de archivo sin caracteres globales. En este caso, eso es lo que quería. Sin embargo, es algo a tener en cuenta.-maxdepth
opción para un hallazgo POSIX.fuente
nullglob
lugar de verificar si un solo resultado es igual al patrón en sí. Algunos patrones pueden coincidir con nombres que son exactamente iguales al patrón en sí (por ejemploa*b
, pero no por ejemploa?b
o[a]
).touch '*py'
), pero esto me señala en otra buena dirección."$M"
como abreviatura para"${M[0]}"
. De lo contrario, bueno, ya tiene la expansión global en una variable de matriz, por lo que debe pasarla a otras cosas como una lista, en lugar de hacer que vuelvan a expandir la globalización.[
proceso) conif [[ $M ]]; then ...
me gusta
Esto es legible y eficiente (a menos que haya una gran cantidad de archivos).
El principal inconveniente es que es mucho más sutil de lo que parece, y a veces me siento obligado a agregar un comentario largo.
Si hay una coincidencia,
"glob*"
se expande por el shell y se pasan todas las coincidenciasexists()
, que comprueba la primera e ignora el resto.Si no hay coincidencia,
"glob*"
se pasaexists()
y tampoco se encuentra allí.Editar: puede haber un falso positivo, ver comentario
fuente
*.[cC]
(puede haber noc
oC
archivo, sino un archivo llamado*.[cC]
) o falso negativo si el primer archivo expandido desde que es, por ejemplo, un enlace a un archivo unexistent o en un archivo en una directorio al que no tiene acceso (usted quiere agregar un|| [ -L "$1" ]
).-e
cuando hay 0 o 1 coincidencias. No funciona para múltiples coincidencias, porque eso se convertiría[ -e file1 file2 ]
y fallaría. Consulte también github.com/koalaman/shellcheck/wiki/SC2144 para conocer los fundamentos y las soluciones sugeridas.Si tienes un conjunto de globfail puedes usar este loco (que realmente no deberías)
o
fuente
(shopt -s failglob; : *) 2>/dev/null && echo exists
test -e tiene la desafortunada advertencia de que considera que los enlaces simbólicos rotos no existen. Por lo tanto, es posible que también desee verificarlos.
fuente
-o
y-a
entest
/[
. Por ejemplo, aquí, falla si$1
es=
con la mayoría de las implementaciones. Usar en su[ -e "$1" ] || [ -L "$1" ]
lugar.Para simplificar un poco la respuesta de MYYN, basada en su idea:
fuente
[a]
, tiene un archivo con nombre[a]
, pero no tiene nombrea
? Todavía me gustanullglob
por esto. Algunos podrían ver esto como pedante, pero también podríamos ser tan correctos como sea razonable.[a]
solo debe coincidira
, no el nombre literal del archivo[a]
.Basado en la respuesta de flabdablet , para mí parece que lo más fácil (no necesariamente el más rápido) es usar find , mientras deja la expansión global en el shell, como:
O en
if
como:fuente
find
respuesta de flabdablet porque acepta rutas en el globo y es más conciso (no requiere,-maxdepth
etc.). También parece mejor que sustat
respuesta porque no continúa haciendo el extrastat
en cada partido adicional de glob. Agradecería si alguien pudiera contribuir casos de esquina donde esto no funciona.-maxdepth 0
porque permite más flexibilidad para agregar condiciones. por ejemplo, suponga que quiero restringir el resultado solo a archivos coincidentes. Podría intentarlofind $glob -type f -quit
, pero eso sería verdadero si el glob NO coincidiera con un archivo, pero sí con un directorio que contuviera un archivo (incluso de forma recursiva). Por el contrariofind $glob -maxdepth 0 -type f -quit
, solo sería verdadero si el glob en sí coincidiera con al menos un archivo. Tenga en cuenta quemaxdepth
no impide que el glob tenga un componente de directorio. (FYI2>
es suficiente. No es necesario&>
)find
en primer lugar es evitar que el shell genere y clasifique una lista potencialmente enorme de coincidencias globales;find -name ... -quit
coincidirá como máximo con un nombre de archivo. Si un script se basa en pasar una lista generada por shell de coincidencias globalesfind
, la invocaciónfind
no logra más que una sobrecarga innecesaria de inicio del proceso. Simplemente probar la lista resultante directamente para no vacío será más rápido y más claro.Tengo otra solución más:
Esto funciona muy bien para mí. ¿Hay algunos casos de esquina que echo de menos?
fuente
En Bash, puede pasar a una matriz; Si el glob no coincide, su matriz contendrá una sola entrada que no corresponde a un archivo existente:
Nota: si ha
nullglob
configurado,scripts
será una matriz vacía, y debe probar con[ "${scripts[*]}" ]
o con[ "${#scripts[*]}" != 0 ]
. Si está escribiendo una biblioteca que debe funcionar con o sinnullglob
, querráUna ventaja de este enfoque es que tiene la lista de archivos con los que desea trabajar, en lugar de tener que repetir la operación global.
fuente
if [ -e "${scripts[0]}" ]...
? ¿También está permitiendo la posibilidad del conjunto de sustantivos de opción de shell ?nounset
está activo. Además, puede ser (ligeramente) más barato probar que la cadena no está vacía que verificar la presencia de un archivo. Sin embargo, es poco probable, dado que acabamos de realizar un glob, lo que significa que el contenido del directorio debe estar fresco en la memoria caché del sistema operativo.Esta abominación parece funcionar:
Probablemente requiere bash, no sh.
Esto funciona porque la opción nullglob hace que el glob se evalúe como una cadena vacía si no hay coincidencias. Por lo tanto, cualquier salida no vacía del comando echo indica que el globo coincide con algo.
fuente
if [ "`echo *py`" != "*py"]
*py
.py
,`echo *py`
se evaluará a*py
.*py
, que es el resultado incorrecto.*py
, ¿tu script hará eco de "Glob coincidente"?No vi esta respuesta, así que pensé en ponerla ahí:
fuente
Explicación
Cuando no hay una coincidencia para
glob*
, entonces$1
contendrá'glob*'
. La prueba-f "$1"
no será verdadera porque elglob*
archivo no existe.¿Por qué esto es mejor que las alternativas?
Esto funciona con sh y derivados: ksh y bash. No crea ningún sub-shell.
$(..)
y los`...`
comandos crean un sub-shell; bifurcan un proceso y, por lo tanto, son más lentos que esta solución.fuente
Me gusta esto en intento(archivos de prueba que contienen
pattern
):Es mucho mejor que
compgen -G
: porque podemos discriminar más casos y con mayor precisión.Puede funcionar con un solo comodín
*
fuente
Tenga en cuenta que esto puede llevar mucho tiempo si hay muchas coincidencias o si el acceso al archivo es lento.
fuente
[a]
se usa un patrón como cuando el archivo[a]
está presente y el archivoa
está ausente. Dirá "encontrado" a pesar de que el único archivo con el que debe coincidira
no está realmente presente.fuente
nullglob
opción de shell.nullglob
un kludge. Comparar un solo resultado con el patrón original es un error (y propenso a resultados falsos), el usonullglob
no lo es.nullglob
un kludge.fuente
glob*
y, por ejemplo, no tiene la escritura para enumerar esos directorios.ls | grep -q "glob.*"
No es la solución más eficiente (si hay una tonelada de archivos en el directorio, puede ser lento), pero es simple, fácil de leer y también tiene la ventaja de que las expresiones regulares son más potentes que los patrones simples de globo de bash.
fuente
fuente