我试图在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?
没有bash没有任何“寻求”重定向的概念。 它在一个长的流中从头到尾读取/写入(大部分)。
如果你碰巧想在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; }
我发现了一种在bash中执行的方法,但是它依赖于exec < /dev/stdin
一个难以理解的特性,它实际上可以按照http://www.madisonlinux.org/pipermail/madlug/2006回退stdin的文件描述符-February / 012896.html :
F=$(mktemp) exec 3<> "$F" rm -f "$F" echo "Hello world" >&3 { exec < /dev/stdin; cat; } <&3
写描述符不受此影响,所以您仍然可以在cat之前将输出追加到描述符3。
可悲的是,我只能在Linux下,而不是在MacOS(BSD)下工作,即使是最新的bash版本。 所以它似乎不是很便携。
尝试改变命令的顺序:
F=$(mktemp tmp.XXXXXX) exec 3<> "$F" echo "Hello world" > "$F" rm -f "$F" #echo "Hello world" >&3 cat <&3
当你用bash打开一个文件描述符时,它可以作为/dev/fd/
一个文件访问。 在这一点上,你可以做cat
,它会从头开始读,或附加( echo "something" >> /dev/fd/3
),它会把它加到最后。 至少在我的系统上,它的行为是这样的。 (另一方面,即使我不写任何描述符,我似乎也无法使“cat <&3”工作。
#!/bin/bash F=$(mktemp tmp.XXXXXX) exec 3<> $F rm $F echo "Hello world" >&3 cat /dev/fd/3
正如其他答案中所建议的那样,在读取文件之前, cat
会回滚文件描述符,因为它认为它只是一个常规文件。
要“倒带”文件描述符,可以简单地使用/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
试着在脚本运行的时候终止这个脚本,你会看到与trap方法相反的结果,这个文件实际上被删除了。