Linux Bash 读/写文件描述符——寻找文件开头
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3838322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bash read/write file descriptors -- seek to start of file
提问by telotortium
I tried to use the read/write file descriptor in bash so that I could delete the file that the file descriptor referred to afterward, as such:
我尝试在 bash 中使用读/写文件描述符,以便我可以删除文件描述符之后引用的文件,如下所示:
F=$(mktemp)
exec 3<> "$F"
rm -f "$F"
echo "Hello world" >&3
cat <&3
but the cat
command gives no output. I can achieve what I want if I use separate file descriptors for reading and writing:
但该cat
命令没有输出。如果我使用单独的文件描述符进行读写,我可以实现我想要的:
F=$(mktemp)
exec 3> "$F"
exec 4< "$F"
rm -f "$F"
echo "Hello world" >&3
cat <&4
which prints Hello world
.
打印Hello world
.
I suspected that bash doesn't automatically seek to the start of the file descriptor when you switch from writing to reading it, and the following combination of bash and python code confirms this:
我怀疑当您从写入切换到读取时 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()
which gives:
这使:
$ bash fdrw.sh
12
$ # This is the prompt reappearing
Is there a way to achieve what I want just using bash?
有没有办法只使用 bash 来实现我想要的?
采纳答案by Ignacio Vazquez-Abrams
No. bash does not have any concept of "seeking" with its redirection. It reads/writes (mostly) from beginning to end in one long stream.
不,bash 的重定向没有任何“寻求”的概念。它在一个长流中从头到尾读/写(大部分)。
回答by yabt
Try changing the sequence of commands:
尝试更改命令顺序:
F=$(mktemp tmp.XXXXXX)
exec 3<> "$F"
echo "Hello world" > "$F"
rm -f "$F"
#echo "Hello world" >&3
cat <&3
回答by telotortium
If you ever do happen to want to seek on bash file descriptors, you can use a subprocess, since it inherits the file descriptors of the parent process. Here is an example C program to do this.
如果您碰巧想要查找 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;
}
回答by David Ongaro
I found a way to do it in bash, but it's relying on an obscure feature of exec < /dev/stdin
which actually can rewind the file descriptor of stdin according to http://linux-ip.net/misc/madlug/shell-tips/tip-1.txt:
我找到了一种在 bash 中执行此操作的方法,但它依赖于一个不起眼的功能,exec < /dev/stdin
该功能实际上可以根据http://linux-ip.net/misc/madlug/shell-tips/tip-1倒带 stdin 的文件描述符.txt:
F=$(mktemp)
exec 3<> "$F"
rm -f "$F"
echo "Hello world" >&3
{ exec < /dev/stdin; cat; } <&3
The write descriptor isn't affected by that so you can still append output to descriptor 3 before the cat.
写入描述符不受此影响,因此您仍然可以在 cat 之前将输出附加到描述符 3。
Sadly I only got this working under Linux not under MacOS (BSD), even with the newest bash version. So it doesn't seem very portable.
遗憾的是,即使使用最新的 bash 版本,我也只能在 Linux 下而不是在 MacOS (BSD) 下使用它。所以它看起来不是很便携。
回答by PSkocik
When you open a file descriptor in bash like that, it becomes accessible as a file in /dev/fd/
.
On that you can do cat
and it'll read from the start, or append (echo "something" >> /dev/fd/3
), and it'll add it to the end.
At least on my system it behaves this way. (On the other hand, I can't seem to be able to get "cat <&3" to work, even if I don't do any writing to the descriptor).
当您像这样在 bash 中打开文件描述符时,它可以作为/dev/fd/
. 您可以这样做cat
,它会从头开始读取,或者附加 ( echo "something" >> /dev/fd/3
),然后将其添加到末尾。至少在我的系统上它的行为是这样的。(另一方面,即使我没有对描述符进行任何写入,我似乎也无法让“cat <&3”起作用)。
回答by sanmai
#!/bin/bash
F=$(mktemp tmp.XXXXXX)
exec 3<> $F
rm $F
echo "Hello world" >&3
cat /dev/fd/3
As suggestedin other answer, cat
will rewind the file descriptor for you before reading from it since it thinks it's just a regular file.
回答by Hexdump
To 'rewind' the file descriptor, you can simply use /proc/self/fd/3
要“倒带”文件描述符,您可以简单地使用 /proc/self/fd/3
Test script :
测试脚本:
#!/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
Try to kill -9 the script while it is running, you will see that contrary to what happens with the trap method, the file is actually deleted.
尝试在脚本运行时 kill -9 脚本,您将看到与陷阱方法发生的情况相反,文件实际上被删除了。