如何测试文件是否可写(CLI)

时间:2016-12-23 19:26:28

标签: dart command-line-interface

我今天在Dart迈出了第一步,我不知道如何继续的第一件事是如何测试作为参数传递给我正在编写的CLI工具的文件是否可写。< / p>

所以我的想法是我有一个接受input目录和输出文件名的工具。它的作用是解析输入目录中的一些文件,将数据编译成有意义的JSON配置并将其保存在output文件中。

但是,在执行任何操作之前,我想运行一个完整性检查,以查看给定的输出文件参数实际上可以用作可写文件。

我决定解决这个问题的方法是在try-catch块中打开Append文件:

try {   
    new File(output).writeAsStringSync('', mode: FileMode.APPEND, flush: true);
} on FileSystemException catch(e) {
    // do something
}

但是,我不喜欢这个解决方案。主要是它创建一个文件,如果它尚不存在。另外,当我只想知道它是否可写时,我不明白为什么我应该在文件中写任何东西。

在Dart中做这件事的正确方法是什么?

2 个答案:

答案 0 :(得分:4)

您可以使用file.statSync().modefile.statSync().modeString()。请参阅FileStat

答案 1 :(得分:1)

实际上很难用任何一种语言可靠地做到这一点。正如 Eiko 所指出的那样,了解文件权限只是故事的一半,因为当前的用户,组和进程决定了这些权限的应用方式。

可能发生的一些极端情况是:

  • 该文件在检查后是可写的,但是在需要进行写操作时(例如,另一个进程更改了权限),该文件不可写。
  • 该文件在检查后不可写,但是在需要进行写操作时才可写。
  • 该文件为只写文件:它存在且不可读,但可以写入。
  • 该文件不存在,并且不允许用户/进程在该目录中创建新文件。
  • 文件系统已以只读模式安装。
  • 父目录不存在。

因此,您编写的任何内容都可能产生误报或误报。

您不添加任何内容的方法是一个很好的简单测试。解决某些问题可能会变得更加复杂,但是在某些情况下,答案总是不是您想要的。

例如,如果您不喜欢在实际写入之前创建文件,请测试该文件是否首先存在:

bool isWritable;
final f = File(filename);
if (f.existsSync()) {
  try {
    // try appending nothing
    f.writeAsStringSync('', mode: FileMode.APPEND, flush: true);
    isWritable = true;
  } on FileSystemException {
    isWritable = false;
  }
} else {
  isWritable = ???; // do you prefer false positive or false negative
  // check if the parent directory exists?
}

// isWritable now, but might not be by the time writing happens

或在测试后将其删除:

bool isWritable;
final f = File(filename);
final didExist = f.existsSync();
try {
  // try appending nothing
  f.writeAsStringSync('', mode: FileMode.APPEND, flush: true);
  isWritable = true;
  if (didExist) {
    f.deleteSync();
  }
} on FileSystemException {
  isWritable = false;
}

// isWritable now, but might not be by the time writing happens

Dart使用异步代码引入了额外的复杂性。

如果使用openWrite方法。它会打开一个流,因此打开文件时不会引发任何写入文件的问题。它们稍后会在使用流或关闭流时发生,这可能与您希望检测到的文件打开代码相距很远。或更糟糕的是,它发生在另一个区域,无法被捕获。

一个有用的技巧是将其打开两次。第一个用于检测文件在关闭时是否可写。第二个是获取将用于写入的流。

try {
  final f = File(filename);

  f.parent.createSync(recursive: true); // create parent(s) if they don't exist

  final tmp = f.openWrite(mode: FileMode.append);
  await tmp.flush();
  await tmp.close(); // errors from opening will be thrown at this point

  // Open it again

  sinkForWritingToTheFile = f.openWrite(mode: FileMode.append);

} on FileSystemException catch (e) {
  // exception from `close` will be caught here
  // exception from the second `openWrite` cannot be caught here
  ...
}