Domanda

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:

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

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

ma il comando cat dà alcun output. Posso ottenere ciò che voglio se uso 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.

I sospetto 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 Python conferma di codice in questo modo:

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 quello che voglio solo usando bash?

È stato utile?

Soluzione

No. bash non ha alcun concetto di "ricerca" con il suo reindirizzamento. Si legge / scrive (per lo più) dall'inizio alla fine in un lungo flusso.

Altri suggerimenti

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;
}

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 standard input in base al http://linux-ip.net/misc/madlug/shell-tips/tip-1.txt :

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

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

Il descrittore di scrittura non è influenzato da che così si può ancora aggiungere l'output al descrittore di 3 prima che il gatto.

Purtroppo ho ottenuto solo questo lavoro sotto Linux non sotto MacOS (BSD), anche con la nuova versione di bash. Così non sembra molto portabile.

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

Quando si apre un descrittore di file in bash così, diventa accessibile come file nella /dev/fd/. Su che si può fare cat e si metterà a letto fin dall'inizio, o append (echo "something" >> /dev/fd/3), e lo aggiungeremo alla fine. Almeno sul mio sistema si comporta in questo modo. (D'altra parte, io non riesco a essere in grado di ottenere "cat <& 3" per il lavoro, anche se non faccio alcuna scrittura al descrittore).

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

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

Come suggerito in altra risposta, cat si riavvolgere il descrittore di file per voi prima di leggere da esso in quanto pensa che sia solo un file regolare.

Per 'rewind' 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

Prova a uccidere -9 lo script mentre è in esecuzione, si vedrà che, contrariamente a quanto avviene con il metodo trappola, il file è effettivamente cancellato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top