Дескрипторы файлов для чтения / записи Bash - ищите начать файл
-
27-09-2019 - |
Вопрос
Я попытался использовать дескриптор файла чтения / записи в 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 Code подтверждает это:
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 программа для этого.
restfd.c.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
который на самом деле может перемотать файл дескриптора 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. Так что это не кажется очень портативным.
Попробуйте изменить последовательность команд:
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
Попробуйте убить -9 сценарий, пока он работает, вы увидите, что вопреки тому, что происходит с методом ловушки, файл фактически удален.