【问题标题】:Check for invalid file descriptor检查无效的文件描述符
【发布时间】:2019-10-11 20:49:25
【问题描述】:

有没有办法在 Go 中检查文件描述符是否有效(在初始打开操作之后)?

如果在命令行标志中指定,请考虑以下打开调试文件的代码片段

    upstreamDebug := flag.String("upstream_debug", "", "File for debugging")
    flag.Parse()

    var upstreamDebugFile os.File
    if *upstreamDebug != "" {
        upstreamDebugFile, err := os.OpenFile(*upstreamDebug, os.O_CREATE|os.O_WRONLY, 0644)
        if err != nil {
            log.Fatal(err)
        }
        log.Println("writing to", upstreamDebugFile.Name())
        defer upstreamDebugFile.Close()
    }

到目前为止,一切都很好。但稍后我们希望写信给upstreamDebugFile,如果 & 仅当它是一个有效的描述符 - 在这个 if { .. } 子句之外。

有没有办法测试它的有效性? lib中似乎没有任何内置方法。与 nil 比较

    if upstreamDebugFile == nil {

报错

cannot convert nil to type os.File

【问题讨论】:

  • 应用程序声明了两个名为 upstreamDebugFile 的变量,这些变量具有不同的类型。通过将外部变量声明为 *os.File 并将内部范围中的短变量声明替换为赋值来修复。
  • 谢谢!是的,我在那里遇到了范围错误;现在修复了。我将 upstreamDebugFile 值用作 io.WriteCloser。我发现它不等于 nil,即使文件没有设置。
  • golang.org/doc/faq#nil_error "只有当 V 和 T 都未设置时,接口值才为零"
  • 发布minimal reproducible example。 cmets 提到了一个 io.WriteCloser,但问题中没有显示。

标签: file go


【解决方案1】:

根据评论:

我将 upstreamDebugFile 值用作 io.WriteCloser。我发现它不等于 nil,即使文件没有设置。

An io.WriteCloser is an instance of an interface。同时,os.OpenFile 返回*os.File,即a pointer to a struct。这实现接口,但不是该接口类型的值。这对某些用途来说没什么大不了的,因为它实现接口这一事实意味着您可以将*os.File 值存储在io.WriteCloser 中。

但是,如以下程序所示,您必须小心测试 io.WriteCloser 是否为 nil,因为它通常不是:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    var iface io.WriteCloser
    p, err := os.OpenFile("/bad/foo", os.O_CREATE, 0666)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("before assignment, iface = %#v, and iface == nil => %t\n", iface, iface == nil)
    iface = p
    fmt.Printf("after assignment, iface = %#v, and iface == nil => %t\n", iface, iface == nil)
    fmt.Printf("meanwhile p == nil => %t\n", p == nil)
}

Go Playground 输出:

open /bad/foo: No such file or directory
before assignment, iface = <nil>, and iface == nil => true
after assignment, iface = (*os.File)(nil), and iface == nil => false
meanwhile p == nil => true

一旦iface 分配了一个类型,你就不能只测试它作为一个整体。你必须开始测试它的实际值,它的底层类型,就像在这个程序中一样:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    var iface io.WriteCloser
    p, err := os.OpenFile("/bad/foo", os.O_CREATE, 0666)
    if err != nil {
        fmt.Println(err)
    }
    iface = p
    fmt.Printf("iface.(*os.File) == nil => %t\n", iface.(*os.File) == nil)
}

Try it on the Go Playground. 但请注意,如果iface 从未被赋值为*os.File 类型的值,则类型断言将失败!

如果您要存储的内容始终为*os.File,只需将您的变量预先声明为*os.File

var foo *os.File
if somecond {
    var err error
    foo, err = os.OpenFile(... arguments ...)
    ...
}
// later:
if foo != nil {
    ...
}

请注意,您也可以这样做:

var iface io.WriteCloser
...
if somecond {
    p, err := os.OpenFile(...)
    ...
    if p != nil {
        iface = p
    }
}

现在iface == nil 又足够了。但除非你真的打算使用多个不同的底层io.WriteCloser 实现,否则请保持简单,直接使用*os.File

【讨论】:

  • 感谢您非常详细的解释。根据上面的@hmm 评论和您的回答,我现在直接测试 *os.File 值,这就是我需要的。
  • 那个类型断言很简洁,但是你never been assigned a value of type *os.File, the type assertion will fail! 的评论让我想知道——实际上有没有一种很好、安全可靠的方法来在使用前检查接口?
  • 当然:使用类型-测试转换,或使用类型切换,或使用reflect(缓慢但完全通用)。使用反射有点棘手。类型测试断言的形式为:x, ok := iface.(sometype)。如果iface的实际类型,当代码被执行时,是sometypex变成那个类型的值,ok变成true。如果不是,x 将成为该类型的零值,ok 将成为 false
  • 类型切换看起来像:switch v := iface.(type) 后跟一堆带有类型的 case 语句。运行的 case 语句是当 v 具有从 iface 中的运行时值获取的给定类型时的语句。有关详细信息,请参阅 golang 规范。
猜你喜欢
  • 1970-01-01
  • 2016-08-12
  • 2010-09-12
  • 2019-06-11
  • 1970-01-01
  • 2017-03-10
  • 1970-01-01
  • 2014-05-01
  • 1970-01-01
相关资源
最近更新 更多