Programmation shell, évitant les fichiers temporaires

8

J'écris souvent des scripts shell KSH qui suivent le même modèle:

  • (1) récupérer la sortie d'une ou plusieurs commandes
  • (2) formatez-le en utilisant grep | cut | awk | sed et imprimez-le à l'écran ou dans un fichier

Pour ce faire, je stocke souvent la sortie de (1) dans un fichier temporaire, puis je fais le formatage en (2) sur ce fichier.

Prenez ce code par exemple:

TMPFILE=file.tmp

# If tmpfile exists rm it.
[ -f $TMPFILE ] && rm -f $TMPFILE

for SERVICE in $(myfunc); do
    getInfo $SERVICE > $TMPFILE # Store raw output in the TMPFILE

    # I retrieve the relevant data from the TMPFILE
    SERV_NAME=$(head -1 $TMPFILE | sed -e 's/ $//')
    SERV_HOSTNAME=$(grep HOSTNAME $TMPFILE | cut -d "=" -f2)
    SERV_ARGS=$(grep Arguments $TMPFILE | cut -d ":" -f2)

    print $SERV_NAME $SEP $SERV_HOSTNAME $SEP $SERV_ARGS
    rm -f $TMPFILE #rm the TMPFILE in vue of next iteration
done

Existe-t-il un moyen, en utilisant des canaux, des redirections et autres joyeusetés, d'éviter d'écrire un fichier sur le disque à chaque fois?

Si cela aide, j'utilise la version ksh M-11/16 / 88i

rahmu
la source
3
C'est une bonne forme pour éviter les noms de variables ALL_CAPS dans les scripts shell, et traiter cet espace de noms comme réservé par le shell pour éviter d'altérer des choses importantes comme PATHou d'autres variables shell ou d'environnement. TMPFILEpeut être bien, mais TMPDIRc'est spécial, alors voulez-vous vraiment marcher sur la corde raide?
jw013
Pour la postérité: une autre question qui a été marquée comme un double de celle-ci unix.stackexchange.com/questions/63923/… inclut une réponse impliquant des tuyaux fifo nommés, qui pourraient également être utilisés ici (bien que ce ne soit probablement pas la meilleure option dans ce cas particulier).
goldilocks
@goldilocks: Nous pouvons peut-être fusionner les deux questions en une seule. Pouvons-nous contacter un modérateur pour ce faire?
rahmu
@rahmu: J'ai signalé l'autre question. Je suppose que c'est aux pouvoirs en place maintenant ...
goldilocks

Réponses:

9

Votre code ressemble à un exemple entièrement justifié d'utilisation de fichiers temporaires pour moi. Je resterais: respectez cette approche. La seule chose qui doit vraiment être modifiée est la façon dont vous créez le fichier temporaire. Utilisez quelque chose comme

 TMP=$(tempfile)

ou

 TMP=$(mktemp)

ou au moins

 TMP=/tmp/myscript_$$

De cette façon, vous ne laisserez pas le nom être facilement prédit (sécurité) et éliminerez les interférences de règles entre plusieurs instances du script s'exécutant en même temps.

rozcietrzewiacz
la source
2
pédagogiquement, les guillemets ne sont pas requis pour l'affectation des variables.
glenn jackman du
1
@glenn Certes, dans ce cas, elles ne devraient pas faire de différence, car chacune des commandes produit généralement une chaîne sans espaces. Mais c'est une bonne habitude d'avoir des guillemets dans les cas où vous affectez une sortie de commande à une variable - donc je persiste à la laisser de cette façon.
rozcietrzewiacz
Suppression des guillemets dans le dernier exemple de distinction.
rozcietrzewiacz
3
@roz Non, vous avez raté le point. Les affectations de variables dans le shell sont reconnues avant toute extension et le fractionnement de champ N'EST PAS effectué pour les affectations de variables. Ainsi, var=$(echo lots of spaces); echo "$var"est très bien et devrait produire lots of spacesen sortie. La vraie mise en garde que personne n'a mentionnée est la substitution de commandes supprime toutes les nouvelles lignes de fin. Ce n'est pas un problème ici, et ne compte que si, par exemple, vous avez eu un cassé mktempqui a créé des noms de fichiers avec des retours à la ligne de fin. Si nécessaire, le travail habituel est var=$(echo command with trailing newline; echo x); var=${var%x}.
jw013
1
@ jw013 Oui, je m'en rends compte maintenant - non, quand j'ai écrit la réponse il y a un an. Merci de l'avoir signalé! (Fixation ...)
rozcietrzewiacz
5

Vous pouvez utiliser une variable:

info="$(getInfo $SERVICE)"
SERV_NAME="$(head -1 $TMPFILE <<<"$info" | sed -e 's/ $//')"
...

De man ksh:

<<<word       A  short  form of here document in which word becomes the
              contents of the here-document after any parameter  expan-
              sion,  command  substitution, and arithmetic substitution
              occur.

Les avantages comprennent:

  • Permet l'exécution parallèle.
  • D'après mon expérience, c'est beaucoup plus rapide que les fichiers temporaires. Sauf si vous avez tellement de données que vous finissez par les échanger, cela devrait être des ordres de grandeur plus rapides (sauf les tampons de mise en cache HD, qui peuvent être à peu près aussi rapides pour de petites quantités de données).
  • D'autres processus ou utilisateurs ne peuvent pas gâcher vos données.
l0b0
la source
<<< ne semble pas exister dans mon ksh. Je reçois une erreur et je n'arrive pas à la trouver dans la page de manuel. J'utilise ksh88. Êtes-vous sûr que cette version devrait avoir cette fonctionnalité?
rahmu
Nan; Je suppose que je n'ai pas vérifié la bonne manpage (il n'y avait aucune mention du numéro de version sur la page Web: /)
l0b0
<<<est bash 'here string'. Je ne pense pas qu'il apparaisse dans un autre shell. (Oh, zshpeut - être ...)
rozcietrzewiacz
2
@rozcietrzewiacz: Google pour man ksh. Cela a certainement été mentionné là-bas.
l0b0
3
Devinez comment bash implémente les chaînes ici et les documents ici. sleep 3 <<<"here string" & lsof -p $! | grep 0rsleep 30251 anthony 0r REG 253,0 12 263271 /tmp/sh-thd-7256597168 (deleted)- oui, il utilise un fichier temporaire.
derobert
2

Vous avez deux options:

  1. Vous récupérez les données une fois (dans votre exemple avec getInfo) et les stockez dans un fichier comme vous le faites.

  2. Vous récupérez les données à chaque fois et ne les stockez pas localement, c'est-à-dire que vous appelez à getInfochaque fois

Je ne vois pas le problème de la création d'un fichier temporaire pour éviter le retraitement / la récupération.

Si vous craignez de laisser le fichier temporaire quelque part, vous pouvez toujours l'utiliser trappour être sûr de le supprimer au cas où le script serait tué / interrompu

trap "rm -f $TMPFILE" EXIT HUP INT QUIT TERM

et utilisez mktemppour créer un nom de fichier unique pour votre fichier temporaire.

Matteo
la source
1

Au lieu de générer un fichier, construisez des instructions d'affectation de shell et évaluez cette sortie.

for SERVICE in $(myfunc); do
    eval $(getInfo $SERVICE |
               sed -n -e '1/\(.*\) *$/SERV_NAME="\1"/p' \
                   -e '/HOSTNAME/s/^[^=]*=\([^=]*\).*/SERV_HOSTNAME="\1"/p' \
                   -e '/Arguments/^[^:]*:\([^:]*\).*/SERV_ARGS="\1"/p')
    print $SERV_NAME $SEP $SERV_HOSTNAME $SED $SERV_ARGS
done

Ou si vous souhaitez simplement imprimer les informations:

for SERVICE in $(myfunc); do
    getInfo $SERVICE | awk -vsep="$SEP" '
        BEGIN{OFS=sep}
        NR == 1 { sub(/ *$/,""); SERV_NAME=$0 }
        /HOSTNAME/ { split($0, HOST, /=/; SERV_HOSTNAME=HOST[2]; }
        /Arguments/ { split($0, ARGS, /:/; SERV_ARGS }
        END { print SERV_NAME, SERV_HOSTNAME, SERV_ARGS }'
done
Arcege
la source