我尝试在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?
答案 0 :(得分:8)
如果你碰巧想要搜索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;
}
答案 1 :(得分:8)
我找到了一种在bash中执行此操作的方法,但它依赖于exec < /dev/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
写入描述符不受此影响,因此您仍然可以在cat之前将输出附加到描述符3。
可悲的是,即使使用最新的bash版本,我也只能在Linux下使用MacOS(BSD)。所以它看起来不太便携。
答案 2 :(得分:5)
尝试更改命令序列:
F=$(mktemp tmp.XXXXXX)
exec 3<> "$F"
echo "Hello world" > "$F"
rm -f "$F"
#echo "Hello world" >&3
cat <&3
答案 3 :(得分:4)
没有。 bash没有任何“寻求”重定向的概念。它在一个长流中从头到尾读取/写入(大部分)。
答案 4 :(得分:4)
当您在bash中打开文件描述符时,它可以作为/dev/fd/
中的文件进行访问。
在那你可以cat
,它从头开始阅读,或追加(echo "something" >> /dev/fd/3
),然后将它添加到最后。
至少在我的系统上它的行为就是这样。 (另一方面,即使我没有对描述符进行任何写作,我也似乎无法获得工作,即使我没有写任何内容。)
答案 5 :(得分:2)
#!/bin/bash
F=$(mktemp tmp.XXXXXX)
exec 3<> $F
rm $F
echo "Hello world" >&3
cat /dev/fd/3
As suggested在其他答案中,cat
会在读取文件描述符之前将其回滚,因为它认为它只是一个常规文件。
答案 6 :(得分:0)
要回放&#39;文件描述符,您只需使用/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方法相反,文件实际上已被删除。