2

ファイルをBashで上書きしたい場合、これは簡単です

echo "Hello world" > hosts

これはファイル記述子では機能しないようです

$ exec 3<> hosts

$ echo "Hello world" >&3
$ cat hosts
Hello world

$ echo "Hello world" >&3
$ cat hosts
Hello world
Hello world
4

1 に答える 1

5

そのとおりです。ファイルを開くモードは、シェルが を呼び出すときに決定されますopen(2)。FDをDUP2(任意の言語で) 開いている場合、ファイルを開いたときに設定されたフラグは、開いている FD 間で共有されます。あなたの場合、O_TRUNCファイルが実際に開かれたときにのみ指定できます。

知っておくべき重要なことは、モードとさまざまなフラグが決定されるのは、ファイルが<file>file、または類似のものを使用して開かれたときだけであるということです。修飾子を使用して FD をコピーすると&、元の FD を指す「エイリアス」が基本的に作成され、元の FD と同じ状態がすべて保持されます。ファイルを切り捨てるには、ファイルを再度開く必要があります。

ファイル記述子を簡単に試してみたい場合は、これが私のデバッグ機能です。

lsfd() {
    local ofd=${ofd:-2} target=${target:-$BASHPID}

    while [[ $1 == -* ]]; do
        if [[ -z $2 || $2 == *[![:digit:]]* ]]; then
            cat
            return 1
        fi
        case ${1##+(-)} in
            u)
                shift
                ofd=$1
                shift
                ;;
            t)
                shift
                target=$1
                shift
                ;;
            h|\?|help)
                cat
                return
        esac
    done <<EOF
USAGE: ${FUNCNAME} [-h|-?|--help] [-u <fd>] [ -t <PID> ] [<fd1> <fd2> <fd3>...]

This is a small lsof wrapper which displays the open
file descriptors of the current BASHPID. If no FDs are given,
the default FDs to display are {0..20}. ofd can also be set in the
environment.

    -u <fd>: Use fd for output. Defaults to stderr. Overrides ofd set in the environment.
    -t <PID>: Use PID instead of BASHPID. Overrides "target" set in the environment.
EOF

    IFS=, local -a 'fds=('"${*:-{0..20\}}"')' 'fds=("${fds[*]}")'
    lsof -a -p $target -d "$fds" +f g -- >&${ofd}
}

stdin を閉じたくないのは、stdin を閉じると問題が発生する場合があるためです。最初に保存されます。

 $ ( { lsfd 3; cat <&3; } {savefd}<&0 <<<'hi' 3>&0- <&"${savefd}" )
COMMAND PID  USER   FD   TYPE FILE-FLAG DEVICE SIZE/OFF     NODE NAME
bash    920 ormaaj   3r   REG        LG   0,22        3 59975426 /tmp/sh-thd-8305926351 (deleted)
hi

ご覧のとおり、3>&0-オペレーターを使用して FD 0 を 3 に移動しても、ファイルは開かれたままO_RDONLYです。>orの選択は<コピー記述子では任意であり、演算子の左側の FD が省略されている場合にのみデフォルトを決定するのに役立ちます。

新しい独立した FD を開きたい場合は、次のようなものが機能します。

$ ( {
cat <&4 >/dev/null; lsfd 3 4; echo there >&4; cat </dev/fd/3
} {savefd}<&0 <<<'hi' 3>&0- 4<>/dev/fd/3 <&"${savefd}"
)
COMMAND  PID  USER   FD   TYPE FILE-FLAG DEVICE SIZE/OFF     NODE NAME
bash    2410 ormaaj   3r   REG        LG   0,22        3 59996561 /tmp/sh-thd-8305914274 (deleted)
bash    2410 ormaaj   4u   REG     RW,LG   0,22        3 59996561 /tmp/sh-thd-8305914274 (deleted)
hi
there

これで、FD 4 は、単に複製されるのではなく、FD 3 が指しているファイルに対して実際に「再オープン」されます (上記のように、ファイルが既にunlink(2)'d' されている場合でも)。最初に FD 4 が開かれ、 を使用して最後までシークされcat、次に追加の行がファイルに書き込まれます。一方、FD 3 のシーク位置はまだ先頭にあるため、結果のファイル全体を端末にカテゴライズできます。

同じ原則があなたのケースにも適用できます。

$ { echo 'Hello world'; echo 'hi' >/dev/fd/1; } >hosts; cat hosts
hi

または、2 つの別々のコマンドを使用して、ファイルを 2 回開いて閉じる方がよいでしょうexec

exec絶対に必要でない限り、ファイル記述子を開くためだけに使用することは避けたいと思います。ファイルを明示的に閉じることを忘れないでください。代わりにコマンドのグループ化を使用すると、ファイルは自動的に閉じられ、実際には「スコープ」が与えられます。これは原理的には Python のwithステートメントに似ています。これで多少の混乱は防げたかもしれません。

も参照してください

于 2012-06-17T04:32:33.360 に答える