如何判断文件夹是否存在且可写?

huangapple go评论87阅读模式
英文:

How to tell if folder exists and is writable?

问题

我可以帮你翻译这段代码。以下是翻译的结果:

我想要一个名为func FolderExists(path string) bool的函数,用于判断文件夹是否存在且可写。我已经写出了以下代码:

func FolderExists(path string) bool {
    info, err := os.Stat(path)
    return os.IsExist(err) && info.Mode().IsDir() && info.Mode().???
}

如何判断这个文件夹是否可写?我不能简单地检查文件模式权限(例如,用户写权限的0200),因为那样我就需要检查文件的所有者。在Go语言中是否有一种简单直接的方法来实现这个功能?

对于那些有UNIX背景的人来说,我正在寻找一个非常简单的等价方式:

if [ -d "$n" && -w "$n" ] ; then ... fi

希望对你有帮助!

英文:

I'd like func FolderExists(path string) bool that will tell whether a folder exists and is writable. I've gotten here:

func FolderExists(path string) bool {
	info, err := os.Stat(path)
	return os.IsExist(err) && info.Mode().IsDir() && info.Mode().???
}

How to tell if this folder is writable? I cannot simply check for filemode permissions (e.g. 0200 for user write permission) because then I'd have to check the owner of the file. Is there a straightforward way to do this in Go?

For those with UNIX backgrounds, looking for the equivalent of the very simple:

if [ -d "$n" && -w "$n" ] ; then ... fi

答案1

得分: 17

如果你正在为一个类Unix操作系统编写代码,你可以使用unix.Access(path string, mode uint32) error函数,将常量unix.W_OK作为mode参数传入。

(感谢用户fxxn指出了unix包的添加,如果你想知道如何为Windows编写替代代码,请参考Gautham的答案。)

虽然这个回答解决了你提出的问题,但当你实际访问目录时,你仍然需要检查是否成功,并且也许这就是你想要做的全部。正如kostix所指出的,权限可能在你进行访问检查后发生变化,或者可能出现其他错误。Ricky Zhang在评论中提到,unix.Access在特定情况下可能会返回完全错误的结果。如果你只检查实际操作是否成功,你也可以避免使用unix包中特定于平台的函数。

只有在未来的操作预计会失败时,才有必要进行显式的访问检查。

无论如何,下面是在Linux上检查存在性和访问性的代码,它给出了我期望的结果:

package main

import (
    "fmt"
    "golang.org/x/sys/unix"
)

func writable(path string) bool {
    return unix.Access(path, unix.W_OK) == nil
}

func main() {
    fmt.Println("/etc writable?", writable("/etc"))
    fmt.Println("/tmp writable?", writable("/tmp"))
}
英文:

If you're writing for a Unix-y OS, you can use unix.Access(path string, mode uint32) error with the constant unix.W_OK as the mode.

(Thanks to user fxxn for flagging when the unix package was added, and see Gautham's answer if you want to know how to write alternate code for Windows.)

Though that answers to the question as asked, you still need to check for success when you actually access the directory, and maybe that's all you want to do. As kostix notes, permissions could change after you do the access check, or there could be an unrelated error. Ricky Zhang says in a comment that unix.Access can return an outright wrong result in specific situations with NFS. If you only check that the actual operation succeeds, you also avoid having to fuss with platform-specific functions from the unix package.

An explicit access check is probably only warranted if it's especially helpful to quit early when a future operation is expected to fail.

Anyway, here's <!-- wonderwall --> code that checks for existence and access that gets me the expected result on Linux:

package main

import (
    &quot;fmt&quot;
    &quot;golang.org/x/sys/unix&quot;
)

func writable(path string) bool {
    return unix.Access(path, unix.W_OK) == nil
}

func main() {
    fmt.Println(&quot;/etc writable?&quot;, writable(&quot;/etc&quot;))
    fmt.Println(&quot;/tmp writable?&quot;, writable(&quot;/tmp&quot;))
}

答案2

得分: 7

这里没有一种平台无关的方法来实现这个,因为在Windows上无法检查用户是否创建了一个目录。因此,我们将为Windows和非Windows系统分别实现,并使用Golang的构建约束根据操作系统条件编译文件。

在file-perm目录下创建3个.go文件,如下所示:

file-perm /
file-perm.go
is-writable.go
is-writable_windows.go

文件:file-perm.go

package main

import (
	"fmt"
)

func main() {
	path := "/tmp/xyz"

	fmt.Println(IsWritable(path))
}

文件:is-writable.go

这个文件在除Windows以外的所有平台上构建,因为syscall.Stat_t在Windows上不可用。请注意,在构建约束**// +build !windows**之后需要有一个空行。

// +build !windows

package main

import (
	"fmt"
	"os"
	"syscall"
)

func IsWritable(path string) (isWritable bool, err error) {
	isWritable = false
	info, err := os.Stat(path)
	if err != nil {
		fmt.Println("路径不存在")
		return
	}

	err = nil
	if !info.IsDir() {
		fmt.Println("路径不是一个目录")
		return
	}

	// 检查文件权限中是否启用了用户位
	if info.Mode().Perm()&(1<<(uint(7))) == 0 {
		fmt.Println("用户对该文件没有设置写权限位")
		return
	}

	var stat syscall.Stat_t
	if err = syscall.Stat(path, &stat); err != nil {
		fmt.Println("无法获取stat")
		return
	}

	err = nil
	if uint32(os.Geteuid()) != stat.Uid {
		isWritable = false
		fmt.Println("用户没有权限写入该目录")
		return
	}

	isWritable = true
	return
}

文件:is-writable_windows.go

这个文件只会在Windows上构建,因为文件名中有**_windows.go**后缀。更多信息请参考https://golang.org/pkg/go/build/

package main

import (
	"fmt"
	"os"
)

func IsWritable(path string) (isWritable bool, err error) {
	isWritable = false
	info, err := os.Stat(path)
	if err != nil {
		fmt.Println("路径不存在")
		return
	}

	err = nil
	if !info.IsDir() {
		fmt.Println("路径不是一个目录")
		return
	}

	// 检查文件权限中是否启用了用户位
	if info.Mode().Perm()&(1<<(uint(7))) == 0 {
		fmt.Println("用户对该文件没有设置写权限位")
		return
	}

	isWritable = true
	return
}

现在,你必须在file-perm目录中使用go build命令,然后运行可执行文件。请注意,它不能与go run一起使用。

英文:

There's no platform independent way to achieve this because checking whether the user created a directory is not possible on Windows. So, we will implement it differently for Windows and non-Windows system and then use Golang's build constraints to conditionally compile the files depending upon the OS.

Create 3 .go files under directory file-perm as shown -

file-perm /
    file-perm.go
    is-writable.go
    is-writable_windows.go

File : file-perm.go

package main

import (
	&quot;fmt&quot;
)

func main() {
	path := &quot;/tmp/xyz&quot;

	fmt.Println(IsWritable(path))
}

File : is-writable.go

This builds on all platforms except Windows because syscall.Stat_t is not available on Windows. Please note that there needs to be a blank line after the build constraint // +build !windows

// +build !windows

package main

import (
	&quot;fmt&quot;
	&quot;os&quot;
	&quot;syscall&quot;
)

func IsWritable(path string) (isWritable bool, err error) {
	isWritable = false
	info, err := os.Stat(path)
	if err != nil {
		fmt.Println(&quot;Path doesn&#39;t exist&quot;)
		return
	}

	err = nil
	if !info.IsDir() {
		fmt.Println(&quot;Path isn&#39;t a directory&quot;)
		return
	}

	// Check if the user bit is enabled in file permission
	if info.Mode().Perm() &amp; (1 &lt;&lt; (uint(7))) == 0 {
		fmt.Println(&quot;Write permission bit is not set on this file for user&quot;)
		return
	}

	var stat syscall.Stat_t
	if err = syscall.Stat(path, &amp;stat); err != nil {
		fmt.Println(&quot;Unable to get stat&quot;)
		return
	}

	err = nil
	if uint32(os.Geteuid()) != stat.Uid {
		isWritable = false
		fmt.Println(&quot;User doesn&#39;t have permission to write to this directory&quot;)
		return
	}

	isWritable = true
	return
}

File : is-writable_windows.go

This file will build only on Windows because of the _windows.go suffix in the filename. For more info please refer to https://golang.org/pkg/go/build/

package main

import (
	&quot;fmt&quot;
	&quot;os&quot;
)

func IsWritable(path string) (isWritable bool, err error) {
	isWritable = false
	info, err := os.Stat(path)
	if err != nil {
		fmt.Println(&quot;Path doesn&#39;t exist&quot;)
		return
	}

	err = nil
	if !info.IsDir() {
		fmt.Println(&quot;Path isn&#39;t a directory&quot;)
		return
	}

	// Check if the user bit is enabled in file permission
	if info.Mode().Perm()&amp;(1&lt;&lt;(uint(7))) == 0 {
		fmt.Println(&quot;Write permission bit is not set on this file for user&quot;)
		return
	}

	isWritable = true
	return
}

> Now, you must use go build in file-perm directory and then run the
> executable. Please note that it won't work with go run.

答案3

得分: 2

我的答案可能不会直接回答你的问题,但还是……

简而言之:不要这样做(除非你在编写嵌入式代码)。解释:文件系统是完全并发的,因此它们天生就容易出现竞争条件。简单来说,一个顺序操作:

  1. 检查文件是否存在。
  2. 如果存在,读取文件。

在现实世界中是行不通的,因为在(1)和(2)之间,文件完全有可能被其他进程删除。

因此,编写上述顺序操作的唯一真实方式是去掉(1),并准备在(2)中处理文件打开错误。

回到你的问题,唯一明智的做法是尝试在目标目录中创建一个文件,并准备处理由于目录不可写而引发的错误。此外,Go语言的错误处理方式特别适用于这种(现实世界的)情况。

英文:

My answer won't answer your question directly but still&hellip;

In short: don't do that (unless maybe you're writing something embedded). Explanation: filesystems are fully concurrent and hence inherently they're a subject for race conditions. To put it simple, an sequence

  1. Check a file exists.
  2. If yes, read it.

Is not gonna work in real world as the file is perfectly able to disappear between (1) and (2) due to some other process deleting it.

So the only real way to write the above sequence is to drop (1) and be prepared to deal with a file open error in (2).

Returning to your problem, the only sensible way to go is to just try to create a file in the destination directory and be prepared to handle an error due to the directory being not writable. Moreover, Go's approach to error handling is specifically tailored for such (real world) situations.

huangapple
  • 本文由 发表于 2013年11月17日 10:05:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/20026320.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定