J'ai un serveur qui reçoit chaque jour un fichier par client dans un répertoire. Les noms de fichiers sont construits comme suit:
uuid_datestring_other-data
Par exemple:
d6f60016-0011-49c4-8fca-e2b3496ad5a7_20160204_023-ERROR
uuid
est un uuid au format standard.datestring
est la sortie dedate +%Y%m%d
.other-data
est de longueur variable mais ne contiendra jamais de soulignement.
J'ai un fichier au format:
#
d6f60016-0011-49c4-8fca-e2b3496ad5a7 client1
d5873483-5b98-4895-ab09-9891d80a13da client2
be0ed6a6-e73a-4f33-b755-47226ff22401 another_client
...
Je dois vérifier que chaque uuid répertorié dans le fichier a un fichier correspondant dans le répertoire, en utilisant bash.
J'en suis là, mais j'ai l'impression de venir dans la mauvaise direction en utilisant une instruction if, et que je dois parcourir les fichiers dans le répertoire source.
Les variables source_directory et uuid_list ont été assignées plus tôt dans le 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}"
Comment vérifier que les fichiers de ma liste existent dans le répertoire? J'aimerais utiliser la fonctionnalité bash autant que possible, mais je ne suis pas contre l'utilisation de commandes si besoin est.
la source
Réponses:
Parcourez les fichiers, créez un tableau associatif sur les uuids contenus dans leurs noms (j'ai utilisé l'expansion des paramètres pour extraire l'uuid). Lisez la liste, vérifiez le tableau associatif pour chaque uuid et indiquez si le fichier a été enregistré ou non.
la source
cd
dans le répertoire du script, mais je me demandais juste pour gagner en connaissances.file=${file##*/}
.Voici une approche plus "timide" et concise:
Notez que bien que ce qui précède soit joli et fonctionnera bien pour quelques fichiers, sa vitesse dépend du nombre d'UUID et sera très lente si vous devez en traiter plusieurs. Si tel est le cas, utilisez la solution de @ choroba ou, pour quelque chose de vraiment rapide, évitez le shell et appelez
perl
:Juste pour illustrer les différences de temps, j'ai testé mon approche bash, choroba et mon perl sur un fichier avec 20000 UUID dont 18001 avaient un nom de fichier correspondant. Notez que chaque test a été exécuté en redirigeant la sortie du script vers
/dev/null
.Mon coup (~ 3,5 min)
Choroba (bash, ~ 0,7 sec)
Mon perl (~ 0,1 sec):
la source
cd
dans le répertoire du script, mais y a-t-il une méthode par laquelle le chemin du fichier pourrait être inclus dans la recherche?${source_directory}
comme vous le faisiez dans votre script."$2"
et passez-le au script comme deuxième argument.C'est du pur Bash (c'est-à-dire pas de commandes externes), et c'est l'approche la plus coïncidente à laquelle je puisse penser.
Mais en termes de performances, ce n'est vraiment pas beaucoup mieux que ce que vous avez actuellement.
Il lira chaque ligne de
path/to/file
; pour chaque ligne, il stockera le premier champ$uuid
et affichera un message si aucun fichier correspondant au modèlepath/to/directory/$uuid*
n'est trouvé:Appelez-le avec
path/to/script path/to/file path/to/directory
.Exemple de sortie à l'aide de l'exemple de fichier d'entrée dans la question sur une hiérarchie de répertoires de test contenant l'exemple de fichier dans la question:
la source
L'idée ici n'est pas de s'inquiéter des erreurs de rapport que le shell rapportera pour vous. Si vous essayez d'
<
ouvrir un fichier qui n'existe pas, votre shell se plaindra. En fait, il ajoutera votre script$0
et le numéro de ligne sur lequel l'erreur s'est produite à la sortie d'erreur quand c'est le cas ... Ce sont de bonnes informations qui sont déjà fournies par défaut - alors ne vous embêtez pas.Vous n'avez pas non plus besoin de prendre le fichier ligne par ligne comme ça - cela peut être terriblement lent. Cela étend le tout en une seule prise de vue à un tableau d'arguments délimité par des espaces blancs et en gère deux à la fois. Si vos données sont conformes à votre exemple, elles
$1
seront toujours votre uuid et$2
seront les vôtres$name
. Si vousbash
pouvez ouvrir une correspondance avec votre uuid - et qu'une seule de ces correspondances existe - alorsprintf
cela se produit. Sinon, ce n'est pas le cas et le shell écrit des diagnostics à stderr pour savoir pourquoi.la source
unset IFS
assure que$(cat <uuid_file)
est divisé sur un espace blanc. Les coquilles se divisent$IFS
différemment lorsqu'elles ne sont constituées que d'espaces blancs ou ne sont pas définies. De telles extensions fractionnées n'ont jamais de champs nuls car toutes les séquences d'espaces blancs ne se présentent que comme un seul délimiteur de champ. Tant qu'il n'y a que deux champs non séparés par des espaces blancs sur chaque ligne, cela devrait fonctionner, je pense. enbash
tout cas.set -f
garantit que l'expansion non cotée n'est pas interprétée pour les globes, et set + f garantit que les globs ultérieurs le sont.<>
car cela crée un fichier inexistant.<
rendra compte comme je le voulais. le problème possible avec cela cependant - et la raison pour laquelle j'ai utilisé incorrectement<>
en premier lieu - est que s'il s'agit d'un fichier pipe sans lecteur ou comme un dev de caractère en ligne, il se bloquera. cela pourrait être évité en traitant la sortie d'erreur plus explicitement et en le faisant[ -f "$dir/$1"* ]
. nous parlons ici des uuids, et donc il ne devrait jamais s'étendre à plus d'un seul fichier. c'est plutôt sympa de voir comment il signale les noms des fichiers en échec à stderr comme ça.<>
serait donc toujours utilisable de cette façon ...<>
est mieux si le glob peut se développer dans un répertoire parce que sur un linux, la lecture / écriture sera échouer et dire - c'est un répertoire.bash
n'acceptera un glob de redirection que s'il ne correspond qu'à un seul fichier. voirman bash
sous REDIRECTION.La façon dont je l'aborderais est d'obtenir d'abord les uuids du fichier, puis d'utiliser
find
Pour la lisibilité,
Exemple avec une liste de fichiers dans
/etc/
, recherchant les noms de fichiers passwd, group, fstab et THISDOESNTEXIST.Puisque vous avez mentionné que le répertoire est plat, vous pouvez utiliser l'
-printf "%f\n"
option pour simplement imprimer le nom de fichier lui-mêmeCe que cela ne fait pas, c'est de répertorier les fichiers manquants.
find
Le petit inconvénient est qu'il ne vous dit pas s'il ne trouve pas de fichier, seulement lorsqu'il correspond à quelque chose. Ce que l'on pourrait faire, cependant, est de vérifier la sortie - si la sortie est vide, alors nous avons un fichier manquantPlus lisible:
Et voici comment il fonctionne comme un petit script:
On pourrait utiliser
stat
comme alternative, car c'est un répertoire plat, mais le code ci-dessous ne fonctionnera pas récursivement pour les sous-répertoires si vous décidez de les ajouter:Si nous prenons l'
stat
idée et l'exécutons, nous pourrions utiliser le code de sortie de stat pour indiquer si un fichier existe ou non. Effectivement, nous voulons faire ceci:Exemple d'exécution:
la source