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í:
- Probar si un globo tiene alguna coincidencia en bash 18 respuestas
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:
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.
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