Question

J'ai essayé d'utiliser la lecture / descripteur de fichier d'écriture dans bash pour que je puisse supprimer le fichier que le descripteur de fichier appelé par la suite, en tant que tel:

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

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

mais la commande cat donne pas de sortie. Je peux obtenir ce que je veux si j'utilise des descripteurs de fichiers séparés pour la lecture et l'écriture:

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

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

qui imprime Hello world.

Je soupçonne que bash ne cherche pas automatiquement au début du descripteur de fichier lorsque vous passez de l'écriture à la lecture, et la combinaison suivante de code bash et python confirme ceci:

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()

ce qui donne:

$ bash fdrw.sh
12

$ # This is the prompt reappearing

Y at-il un moyen d'obtenir ce que je veux juste en utilisant bash?

Était-ce utile?

La solution

Non

. bash n'a pas concept de « recherche » avec sa redirection. Il lit / écrit (surtout) du début à la fin dans un cours d'eau longue.

Autres conseils

Si jamais vous arrive de vouloir rechercher les descripteurs de fichiers bash, vous pouvez utiliser un sous-processus, car il hérite des descripteurs de fichiers du processus parent. Voici un exemple de programme C pour le faire.

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

J'ai trouvé une façon de le faire en bash, mais il se fondant sur une caractéristique obscure de exec < /dev/stdin qui peut effectivement revenir en arrière le descripteur de fichier stdin selon 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

Le descripteur d'écriture est pas affectée par ce que vous pouvez toujours ajouter la sortie au descripteur 3 avant que le chat.

Malheureusement, je ne suis arrivé ce travail sous Linux pas sous MacOS (BSD), même avec la nouvelle version de bash. Donc, il ne semble pas très portable.

Essayez de changer la séquence de commandes:

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

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

Lorsque vous ouvrez un descripteur de fichier dans bash comme ça, il devient accessible en tant que fichier /dev/fd/. Sur ce que vous pouvez faire cat et lirons dès le début, ou append (echo "something" >> /dev/fd/3), et il va ajouter à la fin. Au moins sur mon système, il se comporte de cette façon. (D'autre part, je ne peux pas l'air d'être en mesure d'obtenir « chat <& 3 » au travail, même si je ne fais pas écrit au descripteur).

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

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

Comme suggéré dans d'autres réponses, cat rembobine le descripteur de fichier pour vous avant d'y lire, car il pense qu'il est juste un fichier régulier.

Pour « retour rapide » le descripteur de fichier, vous pouvez simplement utiliser /proc/self/fd/3

script de 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

Essayez de tuer -9 le script en cours d'exécution, vous verrez que contrairement à ce qui se passe avec la méthode de piège, le fichier est effectivement supprimé.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top