Bash,使用文件描述符覆盖? [英] Bash, overwrite using file descriptor?

查看:77
本文介绍了Bash,使用文件描述符覆盖?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果您想用Bash覆盖文件,这很容易

If you want to overwrite a file with Bash, this is easy

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

推荐答案

是正确的.当外壳程序调用open(2)时,确定打开文件的方式.当DUP2 FD(以任何语言)时,打开文件时设置的标志为

That's correct. The mode in which a file is opened is determined when the shell calls open(2). When you DUP2 an FD (in any language), the flags that were set when the file was opened are shared between open FDs. In your case, O_TRUNC can only be specified when the file is actually opened.

要知道的重要一点是,仅当使用<file>file或类似文件打开文件时,才确定模式和各种标志.使用&修饰符复制FD实质上会创建一个指向原始FD的别名",并保留与原始FD相同的状态.截断文件需要重新打开它.

The important thing to know is that the mode and various flags are determined only when the file is opened using <file, >file, or similar. Copying a FD with the & modifier essentially creates an "alias" that points to the original FD, and retains all the same state as the original. Truncating the file requires re-opening it.

如果您想轻松地使用文件描述符,这是我的调试功能:

This is my debugging function if you'd like to play around with file descriptors easily:

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,因为这有时可能会导致问题,所以它会先保存起来.

I like to not close stdin, because this can sometimes cause problems, so it gets saved first.

 $ ( { 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.对于复制描述符,选择><是任意的,并且仅当省略运算符左侧的FD时才用于确定默认值.

As you can see, even if FD 0 is moved to 3 using the 3>&0- operator, the file remains opened O_RDONLY. The choice of > or < is arbitrary with the copy descriptor and only serves to determine the default if the FD on the left of the operator is omitted.

如果您确实想打开一个新的独立FD,则可以执行以下操作:

If you did want to open a new independent FD, something like this could work:

$ ( {
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)了).首先打开FD 4并使用cat搜索到最后,然后将另一行写入文件.同时,FD 3的查找位置仍处于开始位置,因此可以将整个结果文件提供给终端.

Now FD 4 is really "re-opened" to the file FD 3 is pointed at, not just duplicated (even if the file has already been unlink(2)'d, as above). First FD 4 is opened and seeked to the end using cat, then an additional line is written to the file. Meanwhile the seek position of FD 3 is still at the beginning, so the entire resulting file can be cated to the terminal.

同样的原则可以适用于您的情况.

The same principle could be applied to your case.

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

或者甚至更好的办法是,使用两个单独的命令在没有exec的情况下将文件打开和关闭两次.

Or probably even better would be to just open and close the file twice using two separate commands, without exec.

我宁愿避免只使用exec来打开文件描述符,除非绝对必要.您必须记住要显式关闭文件.如果改用命令分组,文件将自动关闭,实际上为它们提供了作用域".这在原则上类似于Python的with语句.那可能避免了一些混乱.

I prefer to avoid using exec just to open file descriptors unless it's absolutely necessary. You have to remember to explicitly close the file. If you use command grouping instead, files will automatically close themselves, in effect giving them a "scope". This is similar in principle to Python's with statement. That might have prevented some confusion.

另请参见:

  • http://wiki.bash-hackers.org/howto/redirection_tutorial
  • http://wiki.bash-hackers.org/syntax/redirection

这篇关于Bash,使用文件描述符覆盖?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆