wildcards test shell sh wildcard

test - Compruebe si existe un archivo con comodín en el script de shell



shell case linux (21)

Esta pregunta ya tiene una respuesta aquí:

Estoy intentando comprobar si existe un archivo, pero con un comodín. Aquí está mi ejemplo:

if [ -f "xorg-x11-fonts*" ]; then printf "BLAH" fi

También lo he probado sin las comillas dobles.


Aquí está mi respuesta:

files=(xorg-x11-fonts*) if [ -e "${files[0]}" ]; then printf "BLAH" fi


Aquí hay una solución para su problema específico que no requiere bucles o comandos externos como ls , find y similares.

if [ "$(echo xorg-x11-fonts*)" != "xorg-x11-fonts*" ]; then printf "BLAH" fi

Como puede ver, es un poco más complicado de lo que esperaba, y se basa en el hecho de que si el shell no puede expandir el globo, significa que no existen archivos con ese globo y el echo emitirá el globo como es , lo que nos permite hacer una mera comparación de cadenas para comprobar si existe alguno de esos archivos.

Sin embargo, si tuviéramos que generalizar el procedimiento , deberíamos tener en cuenta el hecho de que los archivos pueden contener espacios dentro de sus nombres y / o rutas y que el carácter global podría expandirse a la nada (en su ejemplo, ese sería el caso de un archivo cuyo nombre es exactamente xorg-x11-fonts).

Esto podría lograrse mediante la siguiente función, en bash .

function doesAnyFileExist { local arg="$*" local files=($arg) [ ${#files[@]} -gt 1 ] || [ ${#files[@]} -eq 1 ] && [ -e "${files[0]}" ] }

Volviendo a su ejemplo, podría ser invocado así.

if doesAnyFileExist "xorg-x11-fonts*"; then printf "BLAH" fi

La expansión de Glob debe ocurrir dentro de la función en sí misma para que funcione correctamente, es por eso que pongo el argumento entre comillas y para eso está la primera línea en el cuerpo de la función: para que cualquier argumento múltiple (que podría ser el resultado de un glob) la expansión fuera de la función, así como un parámetro espurio) se unirían en uno. Otro enfoque podría ser generar un error si hay más de un argumento, y otro podría ser ignorar todos menos el primer argumento.

La segunda línea en el cuerpo de la función establece los files var en una matriz constituida por todos los nombres de archivos a los que se expandió el globo, uno para cada elemento de la matriz. Está bien si los nombres de los archivos contienen espacios , cada elemento del arreglo contendrá los nombres tal como están , incluidos los espacios.

La tercera línea en el cuerpo de la función hace dos cosas:

  1. Primero verifica si hay más de un elemento en la matriz. Si es así, significa que el globo seguramente se expandió a algo (debido a lo que hicimos en la primera línea), lo que a su vez implica que al menos un archivo que coincida con el globo existe, que es todo lo que queríamos saber.

  2. Si en el paso 1. descubrimos que tenemos menos de 2 elementos en la matriz, verificamos si obtuvimos uno y, de ser así, verificamos si existe, de la forma habitual. Necesitamos hacer esta verificación adicional para tener en cuenta los argumentos de la función sin caracteres globales, en cuyo caso la matriz contiene solo un elemento, no expandido .


El código bash que uso

if ls /syslog/*.log > /dev/null 2>&1; then echo "Log files are present in /syslog/; fi

¡Gracias!


En mi humilde opinión es mejor usar find siempre cuando se comprueban archivos, globos o directorios. El obstáculo al hacerlo es el estado de salida de find : 0 si todas las rutas se recorrieron con éxito,> 0 de lo contrario. La expresión que ha pasado para find no genera eco en su código de salida.

El siguiente ejemplo prueba si un directorio tiene entradas:

$ mkdir A $ touch A/b $ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo ''not empty'' not empty

Cuando A no tiene archivos, grep falla:

$ rm A/b $ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . || echo ''empty'' empty

Cuando A no existe, grep vuelve a fallar porque find solo imprime en stderr:

$ rmdir A $ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo ''not empty'' || echo ''empty'' find: ''A'': No such file or directory empty

Reemplace -not -empty por cualquier otra expresión de find , pero tenga cuidado si -exec un comando que imprima en stdout. Es posible que desee grep para una expresión más específica en tales casos.

Este enfoque funciona muy bien en shell scripts. La pregunta original fue buscar las xorg-x11-fonts* glob xorg-x11-fonts* :

if find -maxdepth 0 -name ''xorg-x11-fonts*'' -print | head -n1 | grep -q . then : the glob matched else : ...not fi

Tenga en cuenta que se llega a else-branched si xorg-x11-fonts* no ha coincidido, o si se encontró un error. Para distinguir el caso use $? .


Encontré un par de soluciones limpias que vale la pena compartir. El primero aún sufre el problema de "esto se romperá si hay demasiadas coincidencias":

pat="yourpattern*" matches=($pat) ; [[ "$matches" != "$pat" ]] && echo "found"

(Recuerde que si usa una matriz sin la sintaxis [ ] , obtendrá el primer elemento de la matriz).

Si tiene "shopt -s nullglob" en su script, simplemente podría hacer:

matches=(yourpattern*) ; [[ "$matches" ]] && echo "found"

Ahora, si es posible tener una tonelada de archivos en un directorio, estás bastante atascado con el uso de encontrar:

find /path/to/dir -maxdepth 1 -type f -name ''yourpattern*'' | grep -q ''.'' && echo ''found''


Estrictamente hablando, si solo quieres imprimir "Blah" aquí está la solución:

find . -maxdepth 1 -name ''xorg-x11-fonts*'' -printf ''BLAH'' -quit

Aquí hay otra manera:

doesFirstFileExist(){ test -e "$1" } if doesFirstFileExist xorg-x11-fonts* then printf "BLAH" fi

Pero creo que lo más óptimo es el siguiente, porque no intentará ordenar los nombres de los archivos:

if [ -z `find . -maxdepth 1 -name ''xorg-x11-fonts*'' -printf 1 -quit` ] then printf "BLAH" fi


La pregunta no era específica de Linux / Bash, así que pensé que agregaría el modo Powershell, que trata a los comodines de manera diferente, lo pones en las citas a continuación:

If (Test-Path "./output/test-pdf-docx/Text-Book-Part-I*"){ Remove-Item -force -v -path ./output/test-pdf-docx/*.pdf Remove-Item -force -v -path ./output/test-pdf-docx/*.docx }

Creo que esto es útil porque el concepto de la pregunta original cubre "shells" en general, no solo Bash o Linux, y se aplicaría a los usuarios de Powershell con la misma pregunta también.


Lo más simple debería ser confiar en el valor de retorno de ls devuelve un valor distinto de cero cuando los archivos no existen):

if ls /path/to/your/files* 1> /dev/null 2>&1; then echo "files do exist" else echo "files do not exist" fi

Redirigí la salida de ls para hacerla completamente silenciosa.

EDITAR: Dado que esta respuesta ha recibido un poco de atención (y comentarios críticos muy útiles como comentarios), aquí hay una optimización que también se basa en la expansión global, pero evita el uso de ls :

for f in /path/to/your/files*; do ## Check if the glob gets expanded to existing files. ## If not, f here will be exactly the pattern above ## and the exists test will evaluate to false. [ -e "$f" ] && echo "files do exist" || echo "files do not exist" ## This is all we needed to know, so we can break after the first iteration break done

Esto es muy similar a la respuesta de @ grok12, pero evita la iteración innecesaria en toda la lista.


Prueba esto

fileTarget="xorg-x11-fonts*" filesFound=$(ls $fileTarget) # 2014-04-03 edit 2: removed dbl-qts around $(...)

edición 2014-04-03 (se eliminaron las citas dobles y se agregó el archivo de prueba ''Charlie 22.html'' (2 espacios)

case ${filesFound} in "" ) printf "NO files found for target=${fileTarget}/n" ;; * ) printf "FileTarget Files found=${filesFound}/n" ;; esac

Prueba

fileTarget="*.html" # where I have some html docs in the current dir FileTarget Files found=Baby21.html baby22.html charlie 22.html charlie21.html charlie22.html charlie23.html fileTarget="xorg-x11-fonts*" NO files found for target=xorg-x11-fonts*

Tenga en cuenta que esto solo funciona en el directorio actual, o donde var fileTarget incluye la ruta que desea inspeccionar.


Puedes hacer lo siguiente:

if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then printf "BLAH" fi

Esto funciona con sh y derivados: ksh y bash. No crea ningún sub-shell. Los comandos $ (..) y `...` crean un sub-shell: bifurcan un proceso y son ineficientes. Por supuesto, funciona con varios archivos, y esta solución puede ser la más rápida, o la segunda más rápida.

Funciona también cuando no hay partidos. No hay necesidad de usar nullglob como dice uno de los comentaristas. $ 1 contendrá el nombre original de la prueba, por lo tanto, la prueba -f $ 1 no tendrá éxito, porque el archivo de $ 1 no existe.


Qué tal si

if ls -l | grep -q ''xorg-x11-fonts.*'' # grep needs a regex, not a shell glob then # do something else # do something else fi


Si hay una gran cantidad de archivos en una carpeta de red, el comodín es cuestionable (velocidad o desbordamiento de argumentos de la línea de comandos).

Terminé con:

if [ -n "$(find somedir/that_may_not_exist_yet -maxdepth 1 -name /*.ext -print -quit)" ] ; then echo Such file exists fi


Si su shell tiene una opción nullglob y está activada, un patrón de comodín que no coincida con ningún archivo se eliminará de la línea de comandos por completo. Esto hará que ls no vea argumentos de ruta de acceso, enumere los contenidos del directorio actual y tenga éxito, lo cual es incorrecto. La estadística GNU, que siempre falla si no se dan argumentos o un argumento que nombra un archivo inexistente, sería más sólido. Además, el operador de redirección &> es un bashism.

if stat --printf='''' /path/to/your/files* 2>/dev/null then echo found else echo not found fi

Aún mejor es GNU find , que puede manejar una búsqueda de comodines internamente y salir tan pronto como encuentra un archivo coincidente, en lugar de perder el tiempo procesando una lista potencialmente enorme de ellos expandida por el shell; esto también evita el riesgo de que el shell pueda desbordar su búfer de línea de comando.

if test -n "$(find /dir/to/search -maxdepth 1 -name ''files*'' -print -quit)" then echo found else echo not found fi

Es posible que las versiones de GNU que no sean de GNU tengan la opción -maxdepth utilizada aquí para hacer que Buscar busque solo la dirección / dir / to / search en lugar de todo el árbol de directorios enraizado allí.


Tal vez esto ayude a alguien:

set -- xorg-x11-fonts* if [ -f "$1" ]; then printf "BLAH" fi


También puede cortar otros archivos

if [ -e $( echo $1 | cut -d" " -f1 ) ] ; then ... fi


Usando nuevas funciones de lujo en ksh, bash y zsh shells (este ejemplo no maneja espacios en los nombres de archivo):

# Declare a regular array (-A will declare an associative array. Kewl!) declare -a myarray=( /mydir/tmp*.txt ) array_length=${#myarray[@]} # Not found if the 1st element of the array is the unexpanded string # (ie, if it contains a "*") if [[ ${myarray[0]} =~ [*] ]] ; then echo "No files not found" elif [ $array_length -eq 1 ] ; then echo "File was found" else echo "Files were found" fi for myfile in ${myarray[@]} do echo "$myfile" done

Sí, esto huele a Perl. Me alegro de no haberlo pisado;)


Yo uso esto:

filescount=`ls xorg-x11-fonts* | awk ''END { print NR }''` if [ $filescount -gt 0 ]; then blah fi


prueba de hombre

if [ -e file ]; then ... fi

trabajará para dir / file.

Saludos


ACTUALIZAR:

Bien, ahora definitivamente tengo la solución:

files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l) if [ "$files" != "0" ] then echo "Exists" else echo "None found." fi > Exists


for i in xorg-x11-fonts*; do if [ -f "$i" ]; then printf "BLAH"; fi done

Esto funcionará con varios archivos y con espacios en blanco en los nombres de los archivos.


if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi