Tengo un servidor que recibe un archivo por cliente cada día en un directorio. Los nombres de archivo se construyen de la siguiente manera:
uuid_datestring_other-data
Por ejemplo:
d6f60016-0011-49c4-8fca-e2b3496ad5a7_20160204_023-ERROR
uuid
Es un formato estándar uuid.datestring
es la salida dedate +%Y%m%d
.other-data
es de longitud variable pero nunca contendrá un guión bajo.
Tengo un archivo del formato:
#
d6f60016-0011-49c4-8fca-e2b3496ad5a7 client1
d5873483-5b98-4895-ab09-9891d80a13da client2
be0ed6a6-e73a-4f33-b755-47226ff22401 another_client
...
Necesito verificar que cada uuid listado en el archivo tenga un archivo correspondiente en el directorio, usando bash.
Llegué hasta aquí, pero siento que vengo de la dirección incorrecta usando una instrucción if, y que necesito recorrer los archivos en el directorio de origen.
Las variables source_directory y uuid_list se han asignado anteriormente en el script:
# Check the entries in the file list
while read -r uuid name; do
# Ignore comment lines
[[ $uuid = \#* ]] && continue
if [[ -f "${source_directory}/${uuid}*" ]]
then
echo "File for ${name} has arrived"
else
echo "PANIC! - No File for ${name}"
fi
done < "${uuid_list}"
¿Cómo debo verificar que los archivos en mi lista existen en el directorio? Me gustaría usar la funcionalidad bash en la medida de lo posible, pero no estoy en contra de usar comandos si es necesario.
command-line
bash
scripts
Arronico
fuente
fuente
Respuestas:
Camine sobre los archivos, cree una matriz asociativa sobre los uuids contenidos en sus nombres (utilicé la expansión de parámetros para extraer el uuid). Luego, lea la lista, verifique la matriz asociativa para cada uuid e informe si el archivo se grabó o no.
fuente
cd
ingresar al directorio dentro de la secuencia de comandos, pero me preguntaba por el simple hecho de adquirir conocimiento.file=${file##*/}
.Aquí hay un enfoque más "bashy" y conciso:
Tenga en cuenta que si bien lo anterior es bonito y funcionará bien para algunos archivos, su velocidad depende de la cantidad de UUID y será muy lento si necesita procesar muchos. Si ese es el caso, use la solución de @ choroba o, para algo realmente rápido, evite el shell y llame
perl
:Solo para ilustrar las diferencias de tiempo, probé mi enfoque bash, choroba's y mi perl en un archivo con 20000 UUID, de los cuales 18001 tenía un nombre de archivo correspondiente. Tenga en cuenta que cada prueba se ejecutó redirigiendo la salida del script a
/dev/null
.Mi fiesta (~ 3.5 min)
Choroba's (bash, ~ 0.7 sec)
Mi perl (~ 0.1 seg):
fuente
cd
ingresar al directorio en el script, pero ¿hay algún método por el cual la ruta del archivo se pueda incluir en la búsqueda?${source_directory}
tal como lo hacía en su secuencia de comandos."$2"
y páselo al guión como segundo argumento.Esto es puro Bash (es decir, sin comandos externos), y es el enfoque más coincidente que se me ocurre.
Pero en términos de rendimiento no es mucho mejor de lo que tiene actualmente.
Leerá cada línea de
path/to/file
; para cada línea, almacenará el primer campo$uuid
e imprime un mensaje si un archivo que coincida con el patrónpath/to/directory/$uuid*
es que no se encontró:Llámalo con
path/to/script path/to/file path/to/directory
.Salida de muestra usando el archivo de entrada de muestra en la pregunta en una jerarquía de directorio de prueba que contiene el archivo de muestra en la pregunta:
fuente
La idea aquí es no preocuparse por informar errores que el shell informará por usted. Si intenta
<
abrir un archivo que no existe, su shell se quejará. De hecho, antepondrá el script$0
y el número de línea en el que ocurrió el error a la salida del error cuando ocurra ... Esta es una buena información que ya se proporciona de manera predeterminada, así que no se moleste.Tampoco necesita tomar el archivo línea por línea de esa manera, puede ser muy lento. Esto expande todo en una sola toma a una matriz de argumentos delimitada por espacios en blanco y maneja dos a la vez. Si sus datos son consistentes con su ejemplo,
$1
siempre serán su uuid y$2
serán suyos$name
. Sibash
puede abrir una coincidencia con su uuid, y solo existe una de esas coincidencias, entoncesprintf
sucede. De lo contrario, no lo hace y el shell escribe diagnósticos para explicar por qué.fuente
unset IFS
asegura que$(cat <uuid_file)
se divide en espacios en blanco. Los depósitos se dividen de manera$IFS
diferente cuando se compone solo de espacios en blanco o no está configurado. Tales expansiones divididas nunca tienen campos nulos porque todas las secuencias de espacios en blanco se presentan como un solo delimitador de campo. Creo que siempre que solo haya dos campos separados por espacios no blancos en cada línea, debería funcionar. enbash
, de todos modos.set -f
garantiza que la expansión sin comillas no se interprete para los globos, y set + f asegura que los globos posteriores sí lo sean.<>
porque eso crea un archivo inexistente.<
informará como lo dije. Sin embargo, el posible problema con eso, y la razón por la que usé incorrectamente<>
en primer lugar, es que si se trata de un archivo de tubería sin un lector o como un char dev con buffer de línea, se bloqueará. eso podría evitarse manejando la salida de error de manera más explícita y haciendo[ -f "$dir/$1"* ]
. estamos hablando de uuids aquí, por lo que nunca debería expandirse a más de un solo archivo. Sin embargo, es un poco agradable cómo informa los nombres de archivos fallidos a stderr así.<>
que aún sería utilizable de esa manera ...<>
es mejor si el globo podría expandirse a un directorio porque en un linux la lectura / escritura será fallar y decir: eso es un directorio.bash
solo aceptará un globo de redireccionamiento si solo coincide con un archivo. verman bash
bajo REDIRECCION.La forma en que lo abordaría es obtener primero los uuids del archivo, luego usar
find
Para legibilidad,
Ejemplo con una lista de archivos en
/etc/
busca de passwd, group, fstab y THISDOESNTEXIST nombres de archivo.Como ha mencionado que el directorio es plano, puede usar la
-printf "%f\n"
opción para imprimir solo el nombre del archivoLo que esto no hace es enumerar los archivos que faltan.
find
La pequeña desventaja es que no le dice si no encuentra un archivo, solo cuando coincide con algo. Sin embargo, lo que se podría hacer es verificar el resultado: si el resultado está vacío, entonces nos falta un archivoMás legible:
Y así es como funciona como un pequeño script:
Se podría usar
stat
como alternativa, ya que es un directorio plano, pero el siguiente código no funcionará recursivamente para subdirectorios si alguna vez decide agregar esos:Si tomamos la
stat
idea y la ejecutamos, podríamos usar el código de salida de stat como indicación de si un archivo existe o no. Efectivamente, queremos hacer esto:Ejecución de muestra:
fuente