質問

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 だけを使用して目的を達成する方法はありますか?

役に立ちましたか?

解決

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

他のヒント

あなたは、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;
}

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

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

悲しいことに、最新の bash バージョンであっても、MacOS (BSD) ではなく Linux でのみ動作しました。そのため、あまり携帯性は高くないようです。

のコマンドの順序を変更してみます:

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)を追加し、それが最後にそれを追加しますよ。 少なくとも、私のシステムでは、このように動作します。 (私は記述子への書き込みを行わない場合でも、一方で、私は、仕事に「猫<&3」を取得することができるように見えることはできません)。

#!/bin/bash
F=$(mktemp tmp.XXXXXX)
exec 3<> $F
rm $F

echo "Hello world" >&3
cat /dev/fd/3
他の回答でを示唆したように、それはそれだけだと思っので、

catはそれから読む前に、あなたのためのファイルディスクリプタを巻き戻します通常のファイルます。

TO '巻き戻し' ファイルディスクリプタは、単に/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スクリプトを試してみてください、あなたはトラップ法で何が起こるかに反しが表示され、ファイルが実際に削除されます。

scroll top