18

我尝试在 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 来实现我想要的?

4

7 回答 7

11

我找到了一种在 bash 中执行此操作的方法,但它依赖于一个晦涩的功能,该功能实际上可以根据http://linux-ip.net/misc/madlug/shell-tips/tip-1exec < /dev/stdin倒回标准输入的文件描述符.txt

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

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

写描述符不受此影响,因此您仍然可以在 cat 之前将输出附加到描述符 3。

遗憾的是,即使使用最新的 bash 版本,我也只能在 Linux 下而不是在 MacOS(BSD)下工作。所以它似乎不是很便携。

于 2014-05-18T21:26:38.140 回答
10

如果您碰巧想查找 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;
}
于 2010-10-03T03:08:57.323 回答
6

不,bash 的重定向没有任何“寻求”的概念。它在一个长流中从头到尾读取/写入(大部分)。

于 2010-10-01T10:45:30.830 回答
5

尝试更改命令顺序:

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

#echo "Hello world" >&3
cat <&3
于 2010-10-01T12:08:01.913 回答
5

当您像这样在 bash 中打开文件描述符时,它可以作为/dev/fd/. 你可以这样做cat,它会从头开始读取,或者附加(echo "something" >> /dev/fd/3),它会将它添加到末尾。至少在我的系统上它的行为方式是这样的。(另一方面,我似乎无法让“cat <&3”工作,即使我没有对描述符进行任何写入)。

于 2015-03-06T12:52:30.060 回答
3
#!/bin/bash
F=$(mktemp tmp.XXXXXX)
exec 3<> $F
rm $F

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

正如其他答案中所建议的那样,cat将在读取文件描述符之前为您倒回文件描述符,因为它认为它只是一个常规文件。

于 2017-01-26T00:42:45.340 回答
0

要“倒回”文件描述符,您可以简单地使用/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,您会看到与使用陷阱方法发生的情况相反,该文件实际上已被删除。

于 2017-11-03T09:09:49.480 回答