Bash чтение / запись файловых дескрипторов - поиск начала файла - PullRequest
10 голосов
/ 01 октября 2010

Я попытался использовать дескриптор файла для чтения / записи в bash, чтобы я мог удалить файл, на который дескриптор файла ссылался впоследствии, как таковой:

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

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

но команда cat не выводит. Я могу добиться того, чего хочу, если для чтения и записи использую отдельные файловые дескрипторы:

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

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

который печатает Hello world.

Я подозревал, что bash не автоматически ищет начало дескриптора файла, когда вы переключаетесь с записи на чтение, и следующая комбинация кода bash и python подтверждает это:

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

, что дает:

$ bash fdrw.sh
12

$ # This is the prompt reappearing

Есть ли способ достичь того, чего я хочу, просто используя bash?

Ответы [ 7 ]

8 голосов
/ 19 мая 2014

Я нашел способ сделать это в bash, но он полагается на скрытую функцию exec < /dev/stdin, которая на самом деле может перематывать файловый дескриптор stdin в соответствии с 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

На дескриптор записи это не влияет, поэтому вы все равно можете добавить вывод в дескриптор 3 перед cat.

К сожалению, я работал только под Linux, а не под MacOS (BSD), даже с самой новой версией bash.Так что это не кажется очень портативным.

8 голосов
/ 03 октября 2010

Если вам когда-нибудь понадобится поиск по дескрипторам файлов bash, вы можете использовать подпроцесс, поскольку он наследует файловые дескрипторы родительского процесса. Вот пример программы на C для этого.

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 голосов
/ 01 октября 2010

Попробуйте изменить последовательность команд:

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

#echo "Hello world" >&3
cat <&3
4 голосов
/ 06 марта 2015

Когда вы открываете файловый дескриптор в bash, он становится доступным как файл в /dev/fd/. На этом вы можете сделать cat, и он будет читать с начала, или добавлять (echo "something" >> /dev/fd/3), и он добавит его в конец. По крайней мере, в моей системе это ведет себя так. (С другой стороны, я не могу заставить работать «cat <& 3», даже если я не пишу в дескриптор). </p>

4 голосов
/ 01 октября 2010

Нет. Bash не имеет понятия «поиск» с его перенаправлением. Он читает / пишет (в основном) от начала до конца в одном длинном потоке.

2 голосов
/ 26 января 2017
#!/bin/bash
F=$(mktemp tmp.XXXXXX)
exec 3<> $F
rm $F

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

Как и предполагалось в другом ответе, cat перемотает дескриптор файла для вас перед чтением из него, так как считает, что это обычный файл.

0 голосов
/ 03 ноября 2017

Чтобы «перемотать» дескриптор файла, вы можете просто использовать /proc/self/fd/3

Тестовый скрипт:

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

Попробуйте убить -9 сценария во время его работы, вы увидите, что вопреки тому, что происходит с методом trap, файл фактически удален.

...