Pregunta

He intentado utilizar el descriptor de archivo de lectura / escritura en bash para que pudiera eliminar el archivo que el descriptor de archivo hace referencia posteriormente, tales como:

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

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

pero el comando cat da ninguna salida. Puedo lograr lo que quiero si uso descriptores de archivos separados para la lectura y la escritura:

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

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

que imprime Hello world.

Yo sospechaba que Bash no busca automáticamente al comienzo del descriptor de archivo cuando se cambia de la escritura a leerlo, y la siguiente combinación de bash y pitón de código confirma esto:

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

lo que da:

$ bash fdrw.sh
12

$ # This is the prompt reappearing

¿Hay una manera de lograr lo que quiero simplemente usando fiesta?

¿Fue útil?

Solución

No. Bash no tiene ningún concepto de "buscar", con su cambio de dirección. Se lee / escribe (en su mayoría) de principio a fin en una corriente de largo.

Otros consejos

Si alguna vez ocurre que desee buscar en los descriptores de fichero bash, se puede utilizar un sub-proceso, ya que hereda los descriptores de archivo del proceso padre. He aquí un ejemplo de programa C para hacer esto.

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

He encontrado una manera de hacerlo en bash, pero está confiando en una característica oscura de exec < /dev/stdin que en realidad puede rebobinar el descriptor de archivo de entrada estándar de acuerdo con la 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

El descriptor de escritura no se ve afectada por que lo que aún puede anexar el resultado de descriptor 3 antes de que el gato.

Lamentablemente sólo tengo este trabajo bajo Linux no en MacOS (BSD), incluso con la versión más reciente golpe. Por lo tanto, no parece muy portátil.

Trate de cambiar la secuencia de comandos:

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

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

Cuando se abre un descriptor de archivo en bash como ese, se hace accesible como un archivo en /dev/fd/. En que se puede hacer cat y va a leer desde el principio, o adición (echo "something" >> /dev/fd/3), y va a añadir a la final. Al menos en mi sistema se comporta de esta manera. (Por otra parte, me parece que no puede ser capaz de obtener "gato <3 y" al trabajo, incluso si no hago cualquier escrito al descriptor).

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

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

Como sugirió en otra respuesta, se cat rebobinar el descriptor de archivo para que antes de leer de él, ya que cree que es justo un archivo normal.

Para 'rebobinado' el descriptor de archivo, puede simplemente usar /proc/self/fd/3

script de prueba:

#!/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

Trate de kill -9 la secuencia de comandos mientras se está ejecutando, verá que, contrariamente a lo que ocurre con el método de captura, el archivo se elimina realmente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top