18

ファイル記述子が後で参照するファイルを削除できるように、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 を使用するだけで目的を達成する方法はありますか?

4

7 に答える 7

11

私はbashでそれを行う方法を見つけましたが、実際にはhttp://linux-ip.net/misc/madlug/shell-tips/tip-1exec < /dev/stdinに従ってstdinのファイル記述子を巻き戻すことができるあいまいな機能に依存しています.txt :

F=$(mktemp)
exec 3<> "$F"
rm -f "$F"

echo "Hello world" >&3
{ exec < /dev/stdin; cat; } <&3

書き込み記述子はその影響を受けないため、cat の前の記述子 3 に出力を追加できます。

悲しいことに、最新の bash バージョンであっても、これは MacOS (BSD) ではなく Linux でしか機能しませんでした。そのため、あまり移植性がないようです。

于 2014-05-18T21:26:38.140 に答える
10

たまたま 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;
}
于 2010-10-03T03:08:57.323 に答える
6

いいえ、bash にはリダイレクトによる「シーク」の概念がありません。1 つの長いストリームで最初から最後まで (ほとんど) 読み取り/書き込みを行います。

于 2010-10-01T10:45:30.830 に答える
5

コマンドの順序を変更してみてください。

F=$(mktemp tmp.XXXXXX)
exec 3<> "$F"
echo "Hello world" > "$F"
rm -f "$F"

#echo "Hello world" >&3
cat <&3
于 2010-10-01T12:08:01.913 に答える