Expanding opportunities standard library std::fs and std::io

Overview

fs_extra

A Rust library that provides additional functionality not present in std::fs.

Build Status Crates.io Status Docs

Documentation

Migrations to 1.x.x version

Key features:

  • Copy files (optionally with information about the progress).

  • Copy directories recursively (optionally with information about the progress).

  • Move files (optionally with information about the progress).

  • Move directories recursively (optionally with information about the progress).

  • A single method for create and write String content in file.

  • A single method for open and read String content from file.

  • Get folder size

  • Get collection of directory entries

Functions:

Function Description
fs_extra::copy_items Recursively copies files and directories from one location to another
fs_extra::copy_items_with_progress Recursively copies files and directories from one location to another with information about progress
fs_extra::move_items Recursively moves files and directories from one location to another
fs_extra::move_items_with_progress Recursively moves files and directories from one location to another with information about progress
fs_extra::remove_items Removes files or directories
fs_extra::file::copy Copies the contents of one file to another
fs_extra::file::copy_with_progress Copies the contents of one file to another with information about progress
fs_extra::file::move_file Moves a file from one location to another
fs_extra::file::move_file_with_progress Moves a file from one location to another with information about progress
fs_extra::file::remove Removes a file
fs_extra::file::read_to_string Reads file content into a String
fs_extra::file::write_all Writes String content to a file
fs_extra::dir::create Creates a new, empty directory at the given path
fs_extra::dir::create_all Recursively creates a directory and all of its parent components if they are missing
fs_extra::dir::copy Recursively copies the directory contents from one location to another
fs_extra::dir::copy_with_progress Recursively copies the directory contents from one location to another with information about progress
fs_extra::dir::move_dir Moves directory contents from one location to another
fs_extra::dir::move_dir_with_progress Moves directory contents from one location to another with information about progress
fs_extra::dir::remove Removes directory
fs_extra::dir::get_size Returns the size of the file or directory
fs_extra::dir::get_dir_content Gets details such as the size and child items of a directory
fs_extra::dir::get_dir_content2 Gets details such as the size and child items of a directory using specified settings
fs_extra::dir::get_details_entry Gets attributes of a directory entry
fs_extra::dir::ls Gets attributes of directory entries in a directory

Usage

Add this to your Cargo.toml:

[dependencies]
fs_extra = "1.2.0"

Examples

The following example shows how to copy a directory recursively and display progress. First a source directory ./temp/dir containing file test1.txt and a subdirectory sub is createad with sub itself having a file test2.txt. ./temp/dir and all contents are then copied out to ./out/dir.

use std::path::Path;
use std::{thread, time};
use std::sync::mpsc::{self, TryRecvError};

extern crate fs_extra;
use fs_extra::dir::*;
use fs_extra::error::*;

fn example_copy() -> Result<()> {

    let path_from = Path::new("./temp");
    let path_to = path_from.join("out");
    let test_folder = path_from.join("test_folder");
    let dir = test_folder.join("dir");
    let sub = dir.join("sub");
    let file1 = dir.join("file1.txt");
    let file2 = sub.join("file2.txt");

    create_all(&sub, true)?;
    create_all(&path_to, true)?;
    fs_extra::file::write_all(&file1, "content1")?;
    fs_extra::file::write_all(&file2, "content2")?;

    assert!(dir.exists());
    assert!(sub.exists());
    assert!(file1.exists());
    assert!(file2.exists());


    let mut options = CopyOptions::new();
    options.buffer_size = 1;
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let handler = |process_info: TransitProcess| {
            tx.send(process_info).unwrap();
            thread::sleep(time::Duration::from_millis(500));
            fs_extra::dir::TransitProcessResult::ContinueOrAbort
        };
        copy_with_progress(&test_folder, &path_to, &options, handler).unwrap();
    });

    loop {
        match rx.try_recv() {
            Ok(process_info) => {
                println!("{} of {} bytes",
                         process_info.copied_bytes,
                         process_info.total_bytes);
            }
            Err(TryRecvError::Disconnected) => {
                println!("finished");
                break;
            }
            Err(TryRecvError::Empty) => {}
        }
    }
    Ok(())

}
fn main() {
    example_copy();
}
Comments
  • Weird(?) behavior when copying whole directories with dir::copy()

    Weird(?) behavior when copying whole directories with dir::copy()

    Inside /my/source/.git there is a non-bare git repo with objects, history, etc... Then I want to copy the contents of that directory to a whole other directory at /my/dest/.git, which is empty.

    let source = PathBuf::from("/my/source/.git");
    let dest = PathBuf::from("/my/dest/.git");
    
    fs_extra::dir::create(&dest, true); // create with erase
    
    let mut copts = fs_extra::dir::CopyOptions::new();
    copts.overwrite = true;
    copts.copy_inside = true;
    
    let _ = fs_extra::dir::copy(&source, &dest, &copts);
    

    After this I would expect to see the following inside /my/dest/.git:

    /
        my/
            dest/
                .git/
                    refs/
                    objects/
                    ...
    

    But instead I see (note nested .git inside .git)

    /
        my/
            dest/
                .git/
                    .git/
                        refs/
                        objects/
                        ...
    

    This looks like the dest path should be the parent directory to where I want to copy the first directory? Documentation states:

    Copies the directory contents from one place to another using recursive method.

    I would assume this means that the contents from source directory become the contents of destination directory, while the directory itself is not copied anywhere?

    I'm using fs_extra 1.1.0 and latest stable Rust.

    opened by rask 9
  • copying content of one folder to another folder

    copying content of one folder to another folder

    hey. I want to copy all files and folders from "a" folder to "b" folder. I tried to use dir::get_dir_content and iterate with ::copy_item, but I couldn't came up with a simple solution. I was able to copy "a" folder inside the "b" folder, but this is not what I want to do.

    Is there a simple solution to this problem? If yes, I would love to see it in examples. If no, an additional function would be great.

    enhancement 
    opened by jaroslaw-weber 9
  • Fix `dir::get_size`'s handling of symlinks

    Fix `dir::get_size`'s handling of symlinks

    ~This makes dir::get_size give a correct calculation of the size of a path of directory, that now matches that returned by eg: du --bytes -s <path>.~ (Due to changes requested during review, this is no longer the case.)

    Before:

    • Symlinks were followed, causing double-counting of symlinked files (see #59).
    • Whilst the metadata size of subdirectory entries was counted correctly, the metadata size of the parent directory (the initial path passed into dir::get_size) was not counted. See: https://github.com/webdesus/fs_extra/issues/25#issuecomment-1172428052

    Now:

    • Symlinks are no longer followed (fixing the regression from #34), so that only the size of the symlink itself is included (as expected), and not the size of the symlink's target.
    • ~If the initial path passed into dir::get_size is a directory, the size of that directory entry itself is now correctly counted (like it was already being counted for subdirectories).~ Due to changes requested during review, this is no longer the case. The metadata size of the directory entries (entries only, not the contents) is now excluded entirely from the total size.

    In addition, the size calculations are performed using DirEntry::metadata, rather than unnecessarily converting to a path and then passing that path to fs::{metadata,symlink_metadata}.

    Fixes #25. Fixes #59.

    opened by edmorley 8
  • copy - `std::convert::From<fs_extra::error::Error>` is not implemented for `std::io::Error`

    copy - `std::convert::From` is not implemented for `std::io::Error`

    I am using standard example from documentation.

    dir::copy(&src, &dst, &dir_options)?;
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<fs_extra::error::Error>` is not implemented for `std::io::Error`
    

    Same for file::copy.

    opened by rofrol 7
  • copy directory and rename

    copy directory and rename

    I hope fs_extra's copy for directory can simulate cp command. e.g., cp d1 d2, if d2 does not exist it copy the whole directory d1 to d2 (in this ase d1 and d2 in same folder with same substructure); if d2 exists, copy d1 into d2 (as what has been implemented). The default behavior can be changed by modifying CopyOptions.

    enhancement 
    opened by hongxuchen 7
  • Unexpected behaviour of dir::get_size

    Unexpected behaviour of dir::get_size

    In my application, I need to get the size of a directory, and in order to avoid having to re-invent the wheel I was planning to use the dir::get_size method provided by this library (thanks for your work!).

    Unfortunately though, the results I am getting differ from the ones expected (I am targeting a Linux machine). This is because the implementation does not return the size of a directory, but the sum of all the sizes of its files, without taking into account the size of the directory structure itself.

    You can quickly test this behaviour on a Linux machine by running the mention method on a given directory, and then running for the same directory the command du --bytes -s {dir_path}.

    If this is the expected behaviour, I would simply suggest to explicitly state it in the documentation in a clear way.

    If this is not the expected behaviour, it can be easily fixed by slightly changing the implementation:

    pub fn get_size<P>(path: P) -> Result<u64>
    where
        P: AsRef<Path>,
    {
        let mut result = 0;
    
        if path.as_ref().is_dir() {
            for entry in read_dir(&path)? {
                let _path = entry?.path();
                if _path.is_file() {
                    result += _path.metadata()?.len();
                } else {
                    result += get_size(_path)?;
                }
            }
            // add the directory data structure size
            result += path.as_ref().metadata()?.len();
        } else {
            result = path.as_ref().metadata()?.len();
        }
        Ok(result)
    }
    

    I understand this will also require some work to update the tests to reflect the new implementation. Please let me know if support is needed for this (can't promise when I'll be able to have a deeper look into this in case it's required).

    opened by gliderkite 5
  • Use Default trait for default values

    Use Default trait for default values

    Using Default trait for default values is probably more idiomatic for Rust. In this case, it has a benefit that if a user doesn't need to change anything from the default value, they can simply do Default::default() rather than having to reference the CopyOptions types.

    opened by upsuper 5
  • get child directories

    get child directories

    Currently DirInfo.directories returns all directories recursively, but I need only to loop through children directories. I thought it was weird that it returns a recursive result by default. Can you add child_directories or change it to get_directories(recursive: bool)?

    enhancement 
    opened by jaroslaw-weber 4
  • Skip_Existing vs Overwrite

    Skip_Existing vs Overwrite

    Hey, what's the difference between SkipExisting and Overwrite? I mean doesn't overwrite selected mean don't skip existing and and not checked means skip existing? fs_extra::dir::CopyOptions

    opened by smallB007 2
  • copy failed when the filepath has space

    copy failed when the filepath has space

    Err(Error { kind: NotFound, message: "Path "/Users/edison/Downloads/123\ aaaa/归档(5)/panda" does not exist or you don't have access!" })

    opened by arlicle 2
  • No such file or directory (os err or 2)

    No such file or directory (os err or 2)

    I'm getting the following error:

    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: NotFound, message: "No such file or directory (os err
    or 2)" }', src/main.rs:4:82
    

    although it should work (I think).

    Reproduction steps

    Create test directory:

    mkdir -p /tmp/rofl1/yeet
    touch /tmp/rofl1/test.txt
    touch /tmp/rofl1/yeet/bruh.txt
    

    Cargo.toml

    [package]
    name = "rust_tmp"
    version = "0.1.0"
    edition = "2021"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    fs_extra = "1.2.0"
    

    src/main.rs

    use fs_extra::dir::CopyOptions;
    
    fn main() {
        fs_extra::dir::copy("/tmp/rofl1", "/tmp/rofl2", &CopyOptions::new()).unwrap();
    }
    

    What am I doing wrong?

    opened by TornaxO7 1
  • Data loss when moving directory on Windows

    Data loss when moving directory on Windows

    Hi, we use fs_extra in Nushell; thanks for writing it!

    We recently had a scary bug report, and it looks like the root cause is fs_extra::dir::move_dir(). If you use move_dir() to change the case of a directory name on Windows (ex: "Test" -> "test"), the directory is deleted and fs_extra reports success.

    For example:

    // This deletes the directory named Test and does not panic!
    fs_extra::dir::move_dir("Test", "test", &options).unwrap();
    

    Steps to Reproduce

    I've put together a minimal repro: https://github.com/rgwood/fs-extra-repro

    Version Info

    fs-extra v1.2.0 Windows 11 (can't reproduce on Linux) rustc v1.63.0

    bug 
    opened by rgwood 4
  • `dir::copy` does not preserve symlinked directories

    `dir::copy` does not preserve symlinked directories

    Summary

    If one of the file entries being copied by fs_extra::dir::copy happens to be a symlink to a directory, then that symlink is converted to a real directory as part of copying, resulting in (a) duplicate file contents in the destination directory, (b) non-identical contents between source and destination (which defeats the point of copying a directory verbatim).

    Note: Symlinks to files are preserved, this is just about symlinks that target directories.

    Steps to reproduce

    Using fs_extra = "=1.2.0" with rustc 1.63.0-beta.3 on macOS 12.4, run the following:

    use fs_extra::dir::CopyOptions;
    use std::fs::{self, File};
    use std::path::{Path, PathBuf};
    
    fn main() {
        let source_dir = PathBuf::from("source");
        let destination_dir = PathBuf::from("destination");
        if source_dir.exists() {
            fs::remove_dir_all(&source_dir).unwrap();
        }
        if destination_dir.exists() {
            fs::remove_dir_all(&destination_dir).unwrap();
        }
    
        let subdir = source_dir.join("subdir");
        fs::create_dir_all(&subdir).unwrap();
        File::create(&subdir.join("file.txt")).unwrap();
        create_dir_symlink("subdir", &source_dir.join("symlink-to-subdir")).unwrap();
    
        let options = &CopyOptions {
            content_only: true,
            ..CopyOptions::default()
        };
        fs_extra::dir::copy(source_dir, destination_dir, options).unwrap();
    }
    
    #[cfg(target_family = "unix")]
    fn create_dir_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> std::io::Result<()> {
        std::os::unix::fs::symlink(original.as_ref(), link.as_ref())
    }
    
    #[cfg(target_family = "windows")]
    fn create_dir_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> std::io::Result<()> {
        std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref())
    }
    

    Expected

    The contents of source/ and destination/ to be identical, and for any symlinks targetting directories to be preserved as symlinks.

    Actual

    The source/ and destination/ directories have different contents, with the symlinked directory now being a real directory containing duplicate files.

    Source:

    $ ls -alR source/
    source/:
    total 0
    drwxr-xr-x  4 emorley staff 128 Jul  6 14:03 .
    drwxr-xr-x 10 emorley staff 320 Jul  6 14:03 ..
    drwxr-xr-x  3 emorley staff  96 Jul  6 14:03 subdir
    lrwxr-xr-x  1 emorley staff   6 Jul  6 14:03 symlink-to-subdir -> subdir
    
    source/subdir:
    total 0
    drwxr-xr-x 3 emorley staff  96 Jul  6 14:03 .
    drwxr-xr-x 4 emorley staff 128 Jul  6 14:03 ..
    -rw-r--r-- 1 emorley staff   0 Jul  6 14:03 file.txt
    

    Destination:

    $ ls -alR destination/
    destination/:
    total 0
    drwxr-xr-x  4 emorley staff 128 Jul  6 14:03 .
    drwxr-xr-x 10 emorley staff 320 Jul  6 14:03 ..
    drwxr-xr-x  3 emorley staff  96 Jul  6 14:03 subdir
    drwxr-xr-x  3 emorley staff  96 Jul  6 14:03 symlink-to-subdir
    
    destination/subdir:
    total 0
    drwxr-xr-x 3 emorley staff  96 Jul  6 14:03 .
    drwxr-xr-x 4 emorley staff 128 Jul  6 14:03 ..
    -rw-r--r-- 1 emorley staff   0 Jul  6 14:03 file.txt
    
    destination/symlink-to-subdir:
    total 0
    drwxr-xr-x 3 emorley staff  96 Jul  6 14:03 .
    drwxr-xr-x 4 emorley staff 128 Jul  6 14:03 ..
    -rw-r--r-- 1 emorley staff   0 Jul  6 14:03 file.txt
    
    opened by edmorley 1
  • Errors in `fs_extra` are different than in `std::fs`

    Errors in `fs_extra` are different than in `std::fs`

              if let Err(e) = fs_extra::dir::move_dir(&thing, &destination_file, &fs_extra::dir::CopyOptions::new()) {
                    messages += format!("Failed to move folder {}, reason {}\n", thing, e).as_str();
                    continue 'next_result;
                }
    

    such code prints(error contains file name)

    Failed to move file /home/rafal/TESTTT/TT/AutoSave_0091.sav, reason Path "/home/rafal/TESTTT/TT/AutoSave_0091.sav" is exist
    

    but such code from std::fs don't print name of file/folder which is I think better, because I can put this name in any other place

            if let Err(e) = fs::metadata("/hehe") {
                messages += format!("HEHE {}, reason {}\n", thing, e).as_str();
                continue 'next_result;
            }
    
    HEHE /hehe reason No such file or directory (os error 2)
    
    opened by qarmin 0
Releases(1.2.0)
  • 1.2.0(Aug 25, 2020)

  • 1.1.0(Jan 4, 2018)

    Release new functionals

    • #5 Copy like cp -r in Unix. For this functionality use copy_inside option of CopyOptions
    • #7 Read directory by levels. For manage read levels use depth option of DirOptions
    • #9 Copy by levels. This opportunity work only for copy operations. Use depth option of CopyOptions
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Mar 8, 2017)

  • 0.3.1(Mar 5, 2017)

  • 0.3.0(Mar 2, 2017)

  • 0.2(Feb 9, 2017)

    1. Added new function:
      • fs_extra::dir::get_dir_content
      • fs_extra::dir::get_size
      • fs_extra::copy_items
      • fs_extra::copy_items_with_progress
      • fs_extra::move_items
      • fs_extra::move_items_with_progress
      • fs_extra::remove_items
    2. fixed some bugs with skip_exist options
    Source code(tar.gz)
    Source code(zip)
  • 0.1(Feb 8, 2017)

Owner
Denis Kurilenko
Denis Kurilenko
Temporary file library for rust

tempfile A secure, cross-platform, temporary file library for Rust. In addition to creating temporary files, this library also allows users to securel

Steven Allen 782 Dec 27, 2022
Extended attribute library for rust.

xattr A small library for setting, getting, and listing extended attributes. Supported Platforms: Linux, MacOS, FreeBSD, and NetBSD. API Documentation

Steven Allen 33 Nov 12, 2022
A POSIX select I/O Multiplexing Rust library.

A POSIX select I/O Multiplexing Rust library.

b23r0 4 Jul 6, 2022
ezio offers an easy to use IO API for reading and writing to files and stdio

ezio - a crate for easy IO ezio offers an easy to use IO API for reading and writing to files and stdio. ezio includes utilities for generating random

Nick Cameron 98 Dec 21, 2022
ergonomic paths and files in rust

path_abs: ergonomic paths and files in rust. This library aims to provide ergonomic path and file operations to rust with reasonable performance. See

Rett Berg 45 Oct 29, 2022
Supertag is a tag-based filesystem, written in Rust, for Linux and MacOS

Supertag is a tag-based filesystem, written in Rust, for Linux and MacOS. It provides a tag-based view of your files by removing the hierarchy constraints typically imposed on files and folders. In other words, it allows you to think about your files not as objects stored in folders, but as objects that can be filtered by folders.

Andrew Moffat 539 Dec 24, 2022
Merge together and efficiently time-sort compressed .pcap files stored in AWS S3 object storage (or locally) to stdout for pipelined processing.

Merge together and efficiently time-sort compressed .pcap files stored in AWS S3 object storage (or locally) to stdout for pipelined processing. High performance and parallel implementation for > 10 Gbps playback throughput with large numbers of files (~4k).

null 4 Aug 19, 2022
rswatch 🔎 is simple, efficient and reliable file watcher.

rswatch File watcher rswatch is a simple, reliable and efficient file watcher, it can watch a single file or a directory and run arbitrary commands wh

Eugene 3 Sep 23, 2022
This library provides a convenient derive macro for the standard library's std::error::Error trait.

derive(Error) This library provides a convenient derive macro for the standard library's std::error::Error trait. [dependencies] therror = "1.0" Compi

Sebastian Thiel 5 Oct 23, 2023
Dfinity's fungible token standard. Any PRs and comments are welcome,collaborate with us to build this standard

Dfinity's fungible token standard. Any PRs and comments are welcome,collaborate with us to build this standard

Deland Labs 46 Nov 7, 2022
Grimsby is an Erlang Port written in Rust that can close its standard input while retaining standard output (and error)

Grimsby An Erlang Port provides the basic mechanism for communication from Erlang with the external world. From the Ports and Port Drivers: Erlang Ref

Peter Morgan 5 May 29, 2023
Rust on ESP32 STD "Hello, World" app. A "Hello, world!" STD binary crate for the ESP32[XX] and ESP-IDF.

Rust on ESP32 STD "Hello, World" app A "Hello, world!" STD binary crate for the ESP32[XX] and ESP-IDF. This is the crate you get when running cargo ne

Ivan Markov 138 Jan 1, 2023
Rust 核心库和标准库的源码级中文翻译,可作为 IDE 工具的智能提示 (Rust core library and standard library translation. can be used as IntelliSense for IDE tools)

Rust 标准库中文版 这是翻译 Rust 库 的地方, 相关源代码来自于 https://github.com/rust-lang/rust。 如果您不会说英语,那么拥有使用中文的文档至关重要,即使您会说英语,使用母语也仍然能让您感到愉快。Rust 标准库是高质量的,不管是新手还是老手,都可以从中

wtklbm 493 Jan 4, 2023
ChatApp made using the standard library net module and tui-rs.

chatui Simple chat application. You'll need both chatui_server and chatui_client to run this application. Installation With cargo cargo install chatui

Gauravsingh Sisodia 6 Dec 15, 2021
CVEs for the Rust standard library

Rust CVE Preface This is a list of CVEs for unsound APIs in the Rust standard library. These bugs break Rust's memory safety guarantee and lead to sec

Yechan Bae 26 Dec 4, 2022
QuickCheck bug hunting in Rust standard library data structures

BugHunt, Rust This project is aiming to provide "stateful" QuickCheck models for Rust's standard library. That is, we build up a random list of operat

Brian L. Troutwine 161 Dec 15, 2022
An example of Brainf*** JIT-compiler with only the standard library.

jit-compiler An example of Brainf*** JIT-compiler with only the standard library. Prerequisite Rust(1.56.0-nightly or later, but it must work kind of

Akihito KIRISAKI 18 Jan 22, 2022
Gostd is the golang standard library implementation in rust-lang.

Gostd Gostd is the golang standard library implementation in rust-lang.

wander 30 Oct 25, 2022
Simple console input macros with the goal of being implemented in the standard library.

Simple console input macros with the goal of being implemented in the standard library.

undersquire 2 Feb 10, 2022
A small random number generator hacked on top of Rust's standard library. An exercise in pointlessness.

attorand from 'atto', meaning smaller than small, and 'rand', short for random. A small random number generator hacked on top of Rust's standard libra

Isaac Clayton 1 Nov 24, 2021