View Source Xfile (xfile v0.2.0)

Xfile contains augmentations of the built-in File module, such as supporting streams, recursion, and programmatic filtering.

Link to this section Summary

Functions

Like the venerable command-line utility, grep searches lines in the given file using the given pattern, returning only the matching lines as a stream.

This function mimics the functionality of grep -rl: it recursively searches all files in the given path, returning only a list of file names (i.e. paths) whose contents have one or more lines that match the pattern.

As Xfile.line_count/1, but returns raw results on success or raises on :error.

Counts the number of lines in the given file, offering functionality similar to wc -l. Directories are not allowed. This is just some sugar around File.stream!/1.

As Xfile.ls/2, but returns raw results on success or raises on :error.

Like File.ls/1, this returns the list of files in the given directory, but it makes available some useful options to support recursive listing and filtering results programmatically.

Link to this section Functions

@spec grep(pattern :: String.pattern() | (String.t() -> boolean()), file :: Path.t()) ::
  Enumerable.t()

Like the venerable command-line utility, grep searches lines in the given file using the given pattern, returning only the matching lines as a stream.

The given pattern can be one of the following:

  • an arity 1 function which returns a boolean; true indicates a match.
  • a pattern compatible with String.contains?/2, i.e. a string, a list of strings, or a regular expression.

Stream

Xfile.grep/2 returns its result as a Stream, so you must remember to convert it to a list via Enum.to_list/1 if you are not lazily evaluating its result.

examples

Examples

iex(1)> Xfile.grep(~r/needle/, "path/to/file")
#Function<59.58486609/2 in Stream.transform/3>

iex(1)> Xfile.grep("dir", ".gitignore") |> Enum.to_list()
["# The directory Mix will write compiled artifacts to.\n",
"# The directory Mix downloads your dependencies sources to.\n"]

Using a function to evaluate file lines:

iex> f = fn line ->
  [serial_number, _] = String.split(line, " ")
  String.to_integer(num) > 214
end
iex> Xfile.grep(f, "file/w/line-numbers") |> Enum.to_list()
["215 Sprocket\n", "216 Gear\n", ...]
Link to this function

grep_rl(pattern, path, opts \\ [])

View Source
@spec grep_rl(pattern :: String.pattern(), path :: Path.t(), opts :: Keyword.t()) ::
  Enumerable.t()

This function mimics the functionality of grep -rl: it recursively searches all files in the given path, returning only a list of file names (i.e. paths) whose contents have one or more lines that match the pattern.

Internally, this relies on grep/2.

Stream

Xfile.grep_rl/3 returns its result as a Stream, so you must remember to convert it to a list via Enum.to_list/1 if you are not lazily evaluating its result.

see-also

See Also

  • grep/2 for searching a single file and returning the matching lines
  • ls/2 using the :filter option to evaluate only the names of the files.
@spec line_count!(file :: Path.t()) :: non_neg_integer() | none()

As Xfile.line_count/1, but returns raw results on success or raises on :error.

@spec line_count(file :: Path.t()) :: {:ok, non_neg_integer()}

Counts the number of lines in the given file, offering functionality similar to wc -l. Directories are not allowed. This is just some sugar around File.stream!/1.

Newlines

This function technically counts new lines, which may result in "off-by-one" errors when the last line of a file is not terminated with a newline.

examples

Examples

iex> Xfile.line_count(".gitignore")
{:ok, 27}
iex> Xfile.line_count("/tmp"}
{:error, :directory}
Link to this function

ls!(directory, opts \\ [])

View Source
@spec ls!(directory :: Path.t(), opts :: Keyword.t()) :: Enumerable.t() | none()

As Xfile.ls/2, but returns raw results on success or raises on :error.

Link to this function

ls(directory, opts \\ [])

View Source
@spec ls(directory :: Path.t(), opts :: Keyword.t()) ::
  {:ok, Enumerable.t()} | {:error, any()}

Like File.ls/1, this returns the list of files in the given directory, but it makes available some useful options to support recursive listing and filtering results programmatically.

Stream

Unlike File.ls/1, Xfile.ls/2 returns its result as a Stream, so you must remember to convert it to a list via Enum.to_list/1 if you are not lazily evaluating its result.

differences-between-file-ls-1

Differences between File.ls/1

options

Options

  • :recursive indicates whether the directory and its subdirectories should be recursively searched. This can be expressed either as a simple boolean or as a positive integer indicating the maximum depth (where false is equivalent to 0 and would list only the contents of the given directory). Default: true

  • :filter can be either a regular expression to be used with String.match?/2 OR an arity 1 function that receives the full file path and returns a boolean value. If the filter operation returns true, the file will be included in the output. Any other output will cause the file to be filtered from the output. Optional.

examples

Examples

Use a regular expression to return only .txt files:

iex> {:ok, stream} = Xfile.ls("path/to/files", filter: ~r/\.txt$/)
{:ok, #Function<59.58486609/2 in Stream.transform/3>}
iex> Enum.to_list(stream)
[
  "path/to/files/a.txt",
  "path/to/files/b.txt",
  "path/to/files/subdir/c.txt"
]

Use a function to apply more complex logic to filter the results:

iex> {:ok, stream} = Xfile.ls("mydir", filter: fn x ->
  stat = File.stat!(x)
  stat.size > 1024
end)
{:ok, #Function<59.58486609/2 in Stream.transform/3>}
iex> Enum.to_list(stream)
[
  "mydir/big-file",
  "mydir/big-file2",
  # ...
]

Limit the depth of the recursion to the given directory and its subdirectories, but no further:

iex> {:ok, stream} = Xfile.ls("top/dir", recursive: 1)
{:ok, #Function<59.58486609/2 in Stream.transform/3>}
iex> Enum.to_list(stream)
[
  "top/dir/a",
  "top/dir/b",
  # ...
  "top/dir/sub1/x",
  "top/dir/sub1/y"
]