A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/com-lihaoyi/os-lib below:

com-lihaoyi/os-lib: OS-Lib is a simple, flexible, high-performance Scala interface to common OS filesystem and subprocess APIs

OS-Lib uses strongly-typed data-structures to represent filesystem paths. The two basic versions are:

Generally, almost all commands take absolute os.Paths. These are basically java.nio.file.Paths with additional guarantees:

Absolute paths can be created in a few ways:

// Get the process' Current Working Directory. As a convention
// the directory that "this" code cares about (which may differ
// from the pwd) is called `wd`
val wd = os.pwd

// A path nested inside `wd` in multiple segments
wd / "folder" / "file"

// The RHS of `/` can have multiple segments if-and-only-if it is a literal string
wd / "folder/file"

// Literal syntax for absolute `os.Path`
val p: os.Path = "/folder/file"

// A path starting from the root
os.root / "folder/file"

// A path with spaces or other special characters
wd / "My Folder/My File.txt"

// Up one level from the wd
wd / os.up

// Up two levels from the wd
wd / os.up / os.up

When constructing os.Paths, the right-hand-side of the / operator must be either a non-literal a string expression containing a single path segment or a literal string containing one-or-more path segments. If a non-literal string expression on the RHS contains multiple segments, you need to wrap the RHS in an explicit os.RelPath(…​) or os.SubPath(…​) constructor to tell OS-Lib how to interpret it. The single-segment limitation is intended to avoid the developer accidentally introducing Directory Traversal Attacks or other related bugs when naively constructing paths out of dynamic and potentially untrusted inputs, which is not an issue for literal string since the string value is directly written in the source code and immediately visible.

os.pwd can be modified in certain scopes via the os.dynamicPwd dynamic variable, but best practice is not to change it. Instead simply define a new path, e.g.

val target = os.pwd / "target"

Should be sufficient for most needs.

Above, we made use of the os.pwd built-in path. There are a number of Paths built into OS-Lib:

os.RelPaths represent relative paths. These are basically defined as:

class RelPath private[ops] (segments0: Array[String], val ups: Int)

The same data structure as Paths, except that they can represent a number of ups before the relative path is applied. They can be created in the following ways:

// The path "folder/file" in multiple segments
val rel1 = os.rel / "folder" / "file"
// RHS of `/` can have multiple segments if-and-only-if it is a literal string
val rel2 = os.rel / "folder/file"
// Literal syntax for `os.RelPath`
val rel3: os.RelPath = "folder/file"

// The path "file"
val rel4 = os.rel / "file"

// The relative difference between two paths
val target = os.pwd / "target/file"
assert((target.relativeTo(os.pwd)) == os.rel / "target/file")

// `up`s get resolved automatically
val minus = os.pwd.relativeTo(target)
val ups = os.up / os.up
assert(minus == ups)

In general, very few APIs take relative paths. Their main purpose is to be combined with absolute paths in order to create new absolute paths. e.g.

val target = os.pwd / "target/file"
val difference = target.relativeTo(os.pwd)
val newBase = os.root / "code/server"
assert(newBase / difference == os.root / "code/server/target/file")

os.up is a relative path that comes in-built:

val target = os.root / "target/file"
assert(target / os.up == os.root / "target")

Note that all paths, both relative and absolute, are always expressed in a canonical manner:

assert((os.root / "folder/file" / os.up).toString == "/folder")
// not "/folder/file/.."

assert((os.rel / "folder/file" / os.up).toString == "folder")
// not "folder/file/.."

So you don’t need to worry about canonicalizing your paths before comparing them for equality or otherwise manipulating them.

os.SubPaths represent relative paths without any .. segments. These are basically defined as:

class SubPath private[ops] (segments0: Array[String])

They can be created in the following ways:

// The path "folder/file" in multiple segments
val sub1 = os.sub / "folder" / "file"
// RHS of `/` can have multiple segments if-and-only-if it is a literal string
val sub2 = os.sub / "folder/file"
// Literal syntax for `os.SubPath`
val sub2: os.Subpath = "folder/file"

// The relative difference between two paths
val target = os.pwd / "out/scratch/file"
assert((target subRelativeTo os.pwd) == os.sub / "out/scratch/file")

// Converting os.RelPath to os.SubPath
val rel3 = os.rel / "folder/file"
val sub4 = rel3.asSubPath

os.SubPaths are useful for representing paths within a particular folder or directory. You can combine them with absolute os.Paths to resolve paths within them, without needing to worry about Directory Traversal Attacks du to accidentally accessing paths outside the destination folder.

val target = os.pwd / "target/file"
val difference = target.relativeTo(os.pwd)
val newBase = os.root / "code/server"
assert(newBase / difference == os.root / "code/server/target/file")

Attempting to construct an os.SubPath with .. segments results in an exception being thrown:

val target = os.pwd / "out/scratch" /

// `up`s are not allowed in sub paths
intercept[Exception](os.pwd subRelativeTo target)

Like os.Paths and os.RelPath, os.SubPaths are always canonicalized and can be compared for equality without worrying about different representations.

OS-Lib’s paths are transparent data-structures, and you can always access the segments and ups directly. Nevertheless, OS-Lib defines a number of useful operations that handle the common cases of dealing with these paths:

In this definition, ThisType represents the same type as the current path; e.g. a Path’s / returns a Path while a RelPath’s / returns a RelPath. Similarly, you can only compare or subtract paths of the same type.

Apart from os.RelPaths themselves, a number of other data structures are convertible into os.RelPaths when spliced into a path using /:

Apart from built-ins like os.pwd or os.root or os.home, you can also construct Paths from Strings, java.io.Files or java.nio.file.Paths:

val relStr = "hello/cow/world/.."
val absStr = "/hello/world"

assert(
  RelPath(relStr) == "hello/cow",
  // Path(...) also allows paths starting with ~,
  // which is expanded to become your home directory
  Path(absStr) == os.root / "hello/world"
)

// You can also pass in java.io.File and java.nio.file.Path
// objects instead of Strings when constructing paths
val relIoFile = new java.io.File(relStr)
val absNioFile = java.nio.file.Paths.get(absStr)

assert(
  RelPath(relIoFile) ==  "hello/cow",
  Path(absNioFile) == os.root / "hello/world",
  Path(relIoFile, root / "base") == os.root / "base/hello/cow"
)

Trying to construct invalid paths fails with exceptions:

val relStr = "hello/.."
intercept[java.lang.IllegalArgumentException]{
  Path(relStr)
}

val absStr = "/hello"
intercept[java.lang.IllegalArgumentException]{
  RelPath(absStr)
}

val tooManyUpsStr = "/hello/../.."
intercept[PathError.AbsolutePathOutsideRoot.type]{
  Path(tooManyUpsStr)
}

As you can see, attempting to parse a relative path with os.Path or an absolute path with os.RelPath throws an exception. If you’re uncertain about what kind of path you are getting, you could use BasePath to parse it :

val relStr = "hello/cow/world/.."
val absStr = "/hello/world"
assert(
  FilePath(relStr) == "hello/cow",
  FilePath(absStr) == os.root / "hello/world"
)

This converts it into a BasePath, which is either a os.Path or os.RelPath. It’s then up to you to pattern-match on the types and decide what you want to do in each case.

You can also pass in a second argument to Path(..., base). If the path being parsed is a relative path, this base will be used to coerce it into an absolute path:

val relStr = "hello/cow/world/.."
val absStr = "/hello/world"
val basePath: FilePath = FilePath(relStr)
assert(
  os.Path(relStr,   os.root / "base") == os.root / "base/hello/cow",
  os.Path(absStr,   os.root / "base") == os.root / "hello/world",
  os.Path(basePath, os.root / "base") == os.root / "base/hello/cow",
  os.Path(".", os.pwd).last != ""
)

For example, if you wanted the common behavior of converting relative paths to absolute based on your current working directory, you can pass in os.pwd as the second argument to Path(...). Apart from passing in Strings or java.io.Files or java.nio.file.Paths, you can also pass in BasePaths you parsed early as a convenient way of converting it to a absolute path, if it isn’t already one.

In general, OS-Lib is very picky about the distinction between relative and absolute paths, and doesn’t allow "automatic" conversion between them based on current-working-directory the same way many other filesystem APIs (Bash, Java, Python, …​) do. Even in cases where it’s uncertain, e.g. you’re taking user input as a String, you have to either handle both possibilities with BasePath or explicitly choose to convert relative paths to absolute using some base.

If you are using a system that supports different roots of paths, e.g. Windows, you can use the argument of os.root to specify which root you want to use. If not specified, the default root will be used (usually, C on Windows, / on Unix).

val root = os.root("C:\\") / "Users/me"
assert(root == os.Path("C:\\Users\\me"))

Additionally, custom filesystems can be specified by passing a FileSystem to os.root. This allows you to use OS-Lib with non-standard filesystems, such as jar filesystems or in-memory filesystems.

val uri = new URI("jar", Paths.get("foo.jar").toURI().toString, null);
val env = new HashMap[String, String]();
env.put("create", "true");
val fs = FileSystems.newFileSystem(uri, env);
val path = os.root("/", fs) / "dir"

Note that the jar file system operations suchs as writing to a file are supported only on JVM 11+. Depending on the filesystem, some operations may not be supported - for example, running an os.proc with pwd in a jar file won’t work. You may also meet limitations imposed by the implementations - in jar file system, the files are created only after the file system is closed. Until that, the ones created in your program are kept in memory.

In addition to manipulating paths on the filesystem, you can also manipulate os.ResourcePath in order to read resources off of the Java classpath. By default, the path used to load resources is absolute, using the Thread.currentThread().getContextClassLoader.

val contents = os.read(os.resource / "test/ammonite/ops/folder/file.txt")
assert(contents.contains("file contents lols"))

You can also pass in a classloader explicitly to the resource call:

val cl = getClass.getClassLoader
val contents2 = os.read(os.resource(cl)/ "test/ammonite/ops/folder/file.txt")
assert(contents2.contains("file contents lols"))

If you want to load resources relative to a particular class, pass in a class for the resource to be relative, or getClass to get something relative to the current class.

val cls = classOf[test.os.Testing]
val contents = os.read(os.resource(cls) / "folder/file.txt")
assert(contents.contains("file contents lols"))

val contents2 = os.read(os.resource(getClass) / "folder/file.txt")
assert(contents2.contains("file contents lols"))

In both cases, reading resources is performed as if you did not pass a leading slash into the getResource("foo/bar") call. In the case of ClassLoader#getResource, passing in a leading slash is never valid, and in the case of Class#getResource, passing in a leading slash is equivalent to calling getResource on the ClassLoader.

OS-Lib ensures you only use the two valid cases in the API, without a leading slash, and not the two cases with a leading slash which are redundant (in the case of Class#getResource, which can be replaced by ClassLoader#getResource) or invalid (a leading slash with ClassLoader#getResource)

Note that you can only use os.read from resource paths; you can’t write to them or perform any other filesystem operations on them, since they’re not really files.

Note also that resources belong to classloaders, and you may have multiple classloaders in your application e.g. if you are running in a servlet or REPL. Make sure you use the correct classloader (or a class belonging to the correct classloader) to load the resources you want, or else it might not find them.

Many operations in OS-Lib operate on os.Sources. These represent values that can provide data which you can then use to write, transmit, etc.

By default, the following types of values can be used where-ever os.Sources are required:

Some operations only work on os.SeekableSource, because they need the ability to seek to specific offsets in the data. Only the following types of values can be used where os.SeekableSource is required:

os.Source also supports anything that implements the Writable interface, such as ujson.Values, uPickle's upickle.default.writable values, or Scalatags's Tags

You can also convert an os.Path or os.ResourcePath to an os.Source via .toSource.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4