2010-10-01 16 views
7

Ho cercato di usare il descrittore di file di lettura/scrittura in bash in modo che potessi eliminare il file che il descrittore di file di cui in seguito, come ad esempio:Bash leggere descrittori di file/scrittura - cercare di avviare di file di

F=$(mktemp) 
exec 3<> "$F" 
rm -f "$F" 

echo "Hello world" >&3 
cat <&3 

ma il comando cat non fornisce output. Posso ottenere ciò che voglio se uso i descrittori di file separati per la lettura e la scrittura:

F=$(mktemp) 
exec 3> "$F" 
exec 4< "$F" 
rm -f "$F" 

echo "Hello world" >&3 
cat <&4 

che stampa Hello world.

sospettavo che bash non cerca automaticamente all'avvio del descrittore di file quando si passa dalla scrittura alla lettura, e la seguente combinazione di bash e il codice python conferma:

fdrw.sh

exec 3<> tmp 
rm tmp 

echo "Hello world" >&3 
exec python fdrw.py 

fdrw.py

import os 

f = os.fdopen(3) 
print f.tell() 
print f.read() 

che dà:

$ bash fdrw.sh 
12 

$ # This is the prompt reappearing 

C'è un modo per ottenere ciò che voglio semplicemente usando bash?

+0

perché dovresti cancellare il file prima di leggere/scrivere? – unhammer

+4

In Unix, quando si rimuove un file, il file non viene effettivamente eliminato finché tutti i descrittori di file aperti non vengono chiusi. Pertanto, l'eliminazione di un file temporaneo subito dopo l'apertura è una pratica comune, poiché garantisce che nessun altro processo possa alterare il file maliziosamente e che il file venga chiuso dopo che il processo ha chiuso il file o chiuso. – telotortium

+0

Perché non ti piace il tuo metodo di avere descrittori di lettura e scrittura separati? Sembra il modo più semplice. – Kelvin

risposta

2

No. bash non ha alcun concetto di "ricerca" con il suo reindirizzamento. Legge/scrive (principalmente) dall'inizio alla fine in un lungo flusso.

+2

Fondamentalmente, quindi, l'unica ragione per i descrittori di lettura/scrittura in bash è di passarli a un processo di esecuzione? – telotortium

+1

Per fornire più canali oltre a stdin, stdout e sterr, sì. –

3

provare a cambiare la sequenza di comandi:

F=$(mktemp tmp.XXXXXX) 
exec 3<> "$F" 
echo "Hello world" > "$F" 
rm -f "$F" 

#echo "Hello world" >&3 
cat <&3 
+0

Quindi scrivere sul file, quindi * cancellare * it, * then * leggere da esso? –

+5

@Dennis questa soluzione funziona davvero. Il gatto non sta leggendo dal file apparentemente cancellato. Sta leggendo dal descrittore che è ancora aperto. È ancora possibile accedere ai contenuti di un file con quel descrittore, anche se l'ultimo (difficile) collegamento ad esso è stato rimosso. – Kelvin

7

Se mai capita di voler cercare su descrittori di file bash, è possibile utilizzare un sottoprocesso, dal momento che eredita i descrittori di file del processo padre. Ecco un esempio di programma C per fare questo.

seekfd.c

#define _FILE_OFFSET_BITS 64 
#include <string.h> 
#include <errno.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main(int argc, char* argv[]) 
{ 
    /* Arguments: fd [offset [whence]] 
    * where 
    * fd: file descriptor to seek 
    * offset: number of bytes from position specified in whence 
    * whence: one of 
    * SEEK_SET (==0): from start of file 
    * SEEK_CUR (==1): from current position 
    * SEEK_END (==2): from end of file 
    */ 
    int fd; 
    long long scan_offset = 0; 
    off_t offset = 0; 
    int whence = SEEK_SET; 
    int errsv; int rv; 
    if (argc == 1) { 
     fprintf(stderr, "usage: seekfd fd [offset [whence]]\n"); 
     exit(1); 
    } 
    if (argc >= 2) { 
     if (sscanf(argv[1], "%d", &fd) == EOF) { 
      errsv = errno; 
      fprintf(stderr, "%s: %s\n", argv[0], strerror(errsv)); 
      exit(1); 
     } 
    } 
    if (argc >= 3) { 
     rv = sscanf(argv[2], "%lld", &scan_offset); 
     if (rv == EOF) { 
      errsv = errno; 
      fprintf(stderr, "%s: %s\n", argv[0], strerror(errsv)); 
      exit(1); 
     } 
     offset = (off_t) scan_offset; 
    } 
    if (argc >= 4) { 
     if (sscanf(argv[3], "%d", &whence) == EOF) { 
      errsv = errno; 
      fprintf(stderr, "%s: %s\n", argv[0], strerror(errsv)); 
      exit(1); 
     } 
    } 

    if (lseek(fd, offset, whence) == (off_t) -1) { 
     errsv = errno; 
     fprintf(stderr, "%s: %s\n", argv[0], strerror(errsv)); 
     exit(2); 
    } 

    return 0; 
} 
5

Ho trovato un modo per farlo in bash, ma è fare affidamento su una caratteristica oscuro exec < /dev/stdin che in realtà in grado di riavvolgere il descrittore di file di stdin in base alle http://www.madisonlinux.org/pipermail/madlug/2006-February/012896.html:

F=$(mktemp) 
exec 3<> "$F" 
rm -f "$F" 

echo "Hello world" >&3 
{ exec < /dev/stdin; cat; } <&3 

Il descrittore di scrittura non è influenzato da quello in modo da poter aggiungere l'output al descrittore 3 prima del gatto.

Purtroppo ho ottenuto questo lavoro su Linux non sotto MacOS (BSD), anche con la versione di bash più recente. Quindi non sembra molto portabile.

3

Quando si apre un descrittore di file in bash in questo modo, diventa accessibile come file in /dev/fd/. Su questo puoi fare cat e leggerà dall'inizio o append (echo "something" >> /dev/fd/3), e lo aggiungerà alla fine. Almeno sul mio sistema si comporta in questo modo. (D'altro canto, non riesco a ottenere il "gatto < & 3" per funzionare, anche se non scrivo sul descrittore).

0
#!/bin/bash 
F=$(mktemp tmp.XXXXXX) 
exec 3<> $F 
rm $F 

echo "Hello world" >&3 
cat /dev/fd/3 

As suggested in altra risposta, cat riavvolge il descrittore di file per voi prima di leggere da esso in quanto pensa che sia solo un file regolare.

0

Per 'riavvolgere' il descrittore di file, si può semplicemente utilizzare /proc/self/fd/3

script di test:

#!/bin/bash 

# Fill data 
FILE=test 
date +%FT%T >$FILE 

# Open the file descriptor and delete the file 
exec 5<>$FILE 
rm -rf $FILE 

# Check state of the file 
# should return an error as the file has been deleted 
file $FILE 

# Check that you still can do multiple reads or additions 
for i in {0..5}; do 
    echo ----- $i ----- 

    echo . >>/proc/self/fd/5 
    cat /proc/self/fd/5 

    echo 
    sleep 1 
done 

cercare di uccidere -9 lo script mentre è in esecuzione, si vedrà che, contrariamente a quanto succede con il metodo trap, il file viene effettivamente cancellato.

Problemi correlati