Programmazione della shell, evitando tempfile


8

Scrivo spesso script di shell KSH che seguono lo stesso schema:

  • (1) recupera l'output da uno o più comandi
  • (2) formattalo usando grep | cut | awk | sed e stampalo sullo schermo o su un file

Per fare ciò, spesso memorizzo l'output di (1) in un file temporaneo, quindi eseguo la formattazione in (2) su quel file.

Prendi quel codice per esempio:

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

C'è un modo, usando pipe, reindirizzamenti e quant'altro, per evitare di scrivere un file su disco ogni volta?

Se aiuta, sto usando la versione ksh M-11/16 / 88i


3
È una buona forma per evitare i nomi delle variabili ALL_CAPS negli script della shell e trattare quello spazio dei nomi come riservato dalla shell per evitare di intasare cose importanti come PATHo altre variabili della shell o dell'ambiente. TMPFILEpuò andare bene, ma TMPDIRè speciale, quindi vuoi davvero camminare sul filo del rasoio?
jw013,

Per i posteri: un'altra domanda che è stata contrassegnata come un duplicato di questo unix.stackexchange.com/questions/63923/… include una risposta che coinvolge i nomi di tubi fifo, che potrebbe anche essere usata qui (anche se probabilmente non è l'opzione migliore in questo caso particolare).
Riccioli d'oro,

@goldilocks: forse possiamo unire le due domande in una sola. Possiamo contattare un moderatore per farlo?
Rahmu,

@rahmu: ho segnalato l'altra domanda. Immagino che dipenda dai poteri che sono ora ...
Riccioli d'oro

Risposte:


9

Il tuo codice sembra un esempio del tutto giustificato dell'uso dei tempfile per me. Starei: attenersi a questo approccio. L'unica cosa che deve davvero essere cambiata è il modo in cui crei il tempfile. Usa qualcosa di simile

 TMP=$(tempfile)

o

 TMP=$(mktemp)

o quantomeno

 TMP=/tmp/myscript_$$

In questo modo non si lascerà che il nome sia facilmente previsto (sicurezza) e si eliminino le interferenze delle regole tra più istanze dello script in esecuzione contemporaneamente.


2
pedanticamente, le quotazioni non sono richieste per l'assegnazione delle variabili.
Glenn Jackman,

1
@glenn Vero, in questo caso non dovrebbero fare la differenza, poiché ciascuno dei comandi produce in genere una stringa senza spazi. Ma è una buona abitudine avere le virgolette nei casi in cui si assegna l'output del comando a una variabile, quindi continuerò a lasciarlo così.
rozcietrzewiacz,

Rimosse le virgolette nell'ultimo esempio per la distinzione.
rozcietrzewiacz,

3
@roz No, hai perso il punto. Le assegnazioni di variabili nella shell vengono riconosciute prima di eseguire qualsiasi espansione e la suddivisione dei campi NON viene eseguita per le assegnazioni di variabili. Pertanto, var=$(echo lots of spaces); echo "$var"va bene e dovrebbe produrre lots of spacescome output. Il vero avvertimento che nessuno ha menzionato è la sostituzione dei comandi che rimuove tutte le nuove righe finali. Questo non è un problema qui, e conta solo per esempio se hai avuto un errore mktempche ha creato nomi di file con nuove righe finali. Il solito lavoro intorno, se necessario, è var=$(echo command with trailing newline; echo x); var=${var%x}.
jw013,

1
@ jw013 Sì, me ne rendo conto ora - non l'ho fatto, quando ho scritto la risposta un anno fa. Grazie per segnalarlo! (correzione ...)
rozcietrzewiacz

5

È possibile utilizzare una variabile:

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

Da 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.

I vantaggi includono:

  • Abilita l'esecuzione parallela.
  • Nella mia esperienza questo è molto più veloce dei file temporanei. A meno che non si disponga di così tanti dati da finire con lo scambio, gli ordini di grandezza dovrebbero essere più veloci (escludendo solo i buffer di memorizzazione nella cache HD, che potrebbero essere altrettanto veloci per piccole quantità di dati).
  • Altri processi o utenti non possono incasinare i tuoi dati.

<<< non sembra esistere nel mio ksh. Ricevo un errore e non riesco a trovarlo nella pagina man. Sto usando ksh88. Sei sicuro che questa versione dovrebbe avere questa funzione?
Rahmu,

No; Immagino di non aver controllato la manpagina giusta (non c'era menzione del numero di versione sulla pagina web: /)
l0b0

<<<è bash 'qui stringa'. Non penso che appaia in nessun altra shell. (Oh, zshforse ...)
rozcietrzewiacz,

2
@rozcietrzewiacz: Google per man ksh. È stato certamente menzionato lì.
l0b0,

3
Indovina come bash implementa here-string e here-docs. sleep 3 <<<"here string" & lsof -p $! | grep 0rsleep 30251 anthony 0r REG 253,0 12 263271 /tmp/sh-thd-7256597168 (deleted)- sì, usa un tempfile.
derobert,

2

Hai due opzioni:

  1. Recuperi i dati una volta (nel tuo esempio con getInfo) e li memorizzi in un file mentre lo fai.

  2. Prendi i dati ogni volta e non li memorizzi localmente, vale a dire che chiami getInfoogni volta

Non vedo il problema nella creazione di un file temporaneo per evitare il rielaborazione / recupero.

Se sei preoccupato di lasciare il file temporaneo da qualche parte, puoi sempre utilizzare trapper essere sicuro di eliminarlo nel caso in cui lo script venga ucciso / interrotto

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

e utilizzare mktempper creare un nome file univoco per il file temporaneo.


1

Invece di generare un file, costruisci istruzioni di assegnazione della shell e valuta quell'output.

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

O se vuoi solo stampare le informazioni:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.