Bash Lese / Schreib-Datei-Deskriptoren - suchen die Datei zu starten
-
27-09-2019 - |
Frage
Ich habe versucht, die Lese / Schreib-Dateideskriptor in bash zu verwenden, so dass ich die Datei löschen konnte, dass der Dateideskriptor später bezeichnet, als solche:
F=$(mktemp)
exec 3<> "$F"
rm -f "$F"
echo "Hello world" >&3
cat <&3
aber der cat
Befehl gibt keine Ausgabe. Ich kann erreichen, was ich will, wenn ich separate Datei-Deskriptoren für das Lesen und Schreiben verwenden:
F=$(mktemp)
exec 3> "$F"
exec 4< "$F"
rm -f "$F"
echo "Hello world" >&3
cat <&4
, die Hello world
druckt.
Ich vermutete, dass bash sucht nicht automatisch zu Beginn des Dateideskriptor, wenn Sie vom Schreiben schalten Sie es zu lesen, und die folgende Kombination von Bash und Python-Code bestätigt dies:
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()
das gibt:
$ bash fdrw.sh
12
$ # This is the prompt reappearing
Gibt es eine Möglichkeit zu erreichen, was ich will bash nur mit?
Lösung
Nein. bash hat kein Konzept der „Suche nach“ mit seiner Umleitung. Es liest / schreibt (meistens) aus in einem langen Strom Anfang bis Ende.
Andere Tipps
Wenn Sie jemals auf bash Filedeskriptoren suchen passieren zu wollen, können Sie einen Subprozess verwenden, da es die Datei-Deskriptoren des Elternprozesses erbt. Hier ist ein Beispiel C-Programm, dies zu tun.
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;
}
Ich fand einen Weg, um es in bash zu tun, aber es ist auf ein obskures Merkmale exec < /dev/stdin
verlassen, die den Dateideskriptor tatsächlich zurückspulen stdin nach 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
Der Schreib Descriptor ist davon nicht betroffen, so dass Sie immer noch Ausgang Descriptor anhängen 3 vor der Katze.
Leider habe ich nur diese Arbeit unter Linux nicht unter MacOS (BSD), auch mit der neuesten Version bash. So ist es nicht sehr tragbar scheint.
Versuchen Sie, die Befehlsfolge zu ändern:
F=$(mktemp tmp.XXXXXX)
exec 3<> "$F"
echo "Hello world" > "$F"
rm -f "$F"
#echo "Hello world" >&3
cat <&3
Wenn Sie ein Datei-Descriptor in bash so zu öffnen, wird es zugänglich als Datei in /dev/fd/
.
Auf dass Sie cat
tun können, und es wird von Anfang an, oder append (echo "something" >> /dev/fd/3
) gelesen, und es wird es bis zum Ende hinzuzufügen.
Zumindest auf meinem System verhält es sich auf diese Weise. (Auf der anderen Seite kann ich nicht in der Lage sein scheinen „cat <& 3“ zur Arbeit zu kommen, auch wenn ich auf den Deskriptor jede Schreiben nicht tun).
#!/bin/bash
F=$(mktemp tmp.XXXXXX)
exec 3<> $F
rm $F
echo "Hello world" >&3
cat /dev/fd/3
Wie vorgeschlagen in anderer Antwort , cat
wird die Dateibeschreibung für Sie zurückspulen vor dem Lesen, da sie es denkt, dass nur eine reguläre Datei.
Um 'rewind' der Dateideskriptor, können Sie einfach /proc/self/fd/3
Test-Skript:
#!/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
Versuchen Sie kill -9 das Skript während er ausgeführt wird, werden Sie, dass im Gegensatz zu sehen, was mit dem Trap-Verfahren geschieht, wird die Datei tatsächlich gelöscht.