Compare commits

...

120 Commits

Author SHA1 Message Date
Thayne McCombs bf37c27a38 Address feedback for removing buffering when running on a single thread 2021-08-09 01:02:30 -06:00
Thayne McCombs fcaebd2295 Merge branch 'master' into no-buffer-single-thread 2021-08-09 01:00:06 -06:00
David Peter c37592b0b7 Add tavianator as a maintainer 2021-08-09 07:34:26 +02:00
David Peter 224b7f2354 Silence wrong clippy warning 2021-08-08 23:48:20 +02:00
David Peter b5344dac30 Improve -x/-X help text and man page description
closes #605
2021-08-08 22:58:13 +02:00
David Peter 018556a367 Add CHANGELOG entries 2021-08-08 15:04:28 +02:00
David Peter aeff525c30 Attempt to fix #365 2021-08-08 15:02:01 +02:00
David Peter 2d398dc4a7 Fix directory-existence check on Windows
This fixes a bug on Windows where `fd` could not be used on ram disks
and encrypted folders.

closes #752
2021-08-08 15:02:01 +02:00
David Peter 6e44828cc8 Add CHANGELOG entry 2021-08-08 13:13:58 +02:00
David Peter b507449146 Handle IO errors based on type, closes #737 2021-08-08 13:10:16 +02:00
David Peter d9697d1486 Make the path-separator check Windows-only 2021-08-08 13:03:31 +02:00
David Peter 515e0ee469 Do not use .clone() 2021-08-08 13:03:31 +02:00
David Peter a0ca460901 Adjust error message 2021-08-08 13:03:31 +02:00
Kaleb Olson 693325bcf2 Adds path separator checking and hint for automatic / expansion for some windows shells, eg., msys 2021-08-08 13:03:31 +02:00
dependabot[bot] ab81cad3b5 Bump regex from 1.4.6 to 1.5.4
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.6 to 1.5.4.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.4.6...1.5.4)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-08 12:14:50 +02:00
dependabot[bot] 70850629e9 Bump globset from 0.4.6 to 0.4.8
Bumps [globset](https://github.com/BurntSushi/ripgrep) from 0.4.6 to 0.4.8.
- [Release notes](https://github.com/BurntSushi/ripgrep/releases)
- [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/ripgrep/compare/globset-0.4.6...globset-0.4.8)

---
updated-dependencies:
- dependency-name: globset
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-08 12:14:41 +02:00
dependabot[bot] 8b0db189a1 Bump ctrlc from 3.1.7 to 3.1.9
Bumps [ctrlc](https://github.com/Detegr/rust-ctrlc) from 3.1.7 to 3.1.9.
- [Release notes](https://github.com/Detegr/rust-ctrlc/releases)
- [Commits](https://github.com/Detegr/rust-ctrlc/compare/3.1.7...3.1.9)

---
updated-dependencies:
- dependency-name: ctrlc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-08 11:48:54 +02:00
dependabot[bot] ad1b267601 Bump filetime from 0.2.13 to 0.2.14
Bumps [filetime](https://github.com/alexcrichton/filetime) from 0.2.13 to 0.2.14.
- [Release notes](https://github.com/alexcrichton/filetime/releases)
- [Commits](https://github.com/alexcrichton/filetime/compare/0.2.13...0.2.14)

---
updated-dependencies:
- dependency-name: filetime
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-08 11:48:47 +02:00
dependabot[bot] 04829c287d Bump anyhow from 1.0.35 to 1.0.42
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.35 to 1.0.42.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.35...1.0.42)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-08 11:43:15 +02:00
dependabot[bot] ca92bea0d7 Bump version_check from 0.9.2 to 0.9.3
Bumps [version_check](https://github.com/SergioBenitez/version_check) from 0.9.2 to 0.9.3.
- [Release notes](https://github.com/SergioBenitez/version_check/releases)
- [Commits](https://github.com/SergioBenitez/version_check/compare/v0.9.2...v0.9.3)

---
updated-dependencies:
- dependency-name: version_check
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-08 11:43:01 +02:00
dependabot[bot] 252beb8df7 Bump libc from 0.2.80 to 0.2.98
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.80 to 0.2.98.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.80...0.2.98)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-08 11:42:55 +02:00
dependabot[bot] b80764b19a Bump regex from 1.4.2 to 1.4.6
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.2 to 1.4.6.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.4.2...1.4.6)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-08 11:27:36 +02:00
Daniel Bast 148fa64a73 Change dependabot interval to monthly 2021-08-08 11:15:18 +02:00
Daniel Bast 6ce55624f7 Add dependabot config for cargo updates 2021-08-08 11:15:18 +02:00
Max Triano a8951f0641 Clarifying comments 2021-07-27 09:13:54 +02:00
Max Triano 5860dedb1e No buffering cmd output when running on 1 thread 2021-07-27 09:13:54 +02:00
Max Triano 7656358f44 Just trying some things out, initial functionality 2021-07-27 09:13:54 +02:00
David Peter 708efaa812 Literal separators 2021-07-27 09:05:23 +02:00
David Peter a7a4499d1d Fix unneeded references 2021-07-27 09:05:23 +02:00
David Peter 2a2126c40a Fix clippy warnings 2021-07-27 09:05:23 +02:00
David Peter db30bfc2b7 Update README.md 2021-07-26 22:34:03 +02:00
David Peter 81a051f07c Fix shell completion path 2021-07-26 22:33:27 +02:00
David Peter 95d6470956 Fix stripping on arm musl target 2021-07-26 22:33:27 +02:00
David Peter e59a2001cc Fix shell completion path 2021-07-26 22:33:27 +02:00
David Peter c60338a28b Adapt test options 2021-07-26 22:33:27 +02:00
David Peter 16464a35b1 Fix man page path 2021-07-26 22:33:27 +02:00
David Peter 1d85e31260 Migrate to GitHub actions 2021-07-26 22:33:27 +02:00
Thayne McCombs 024309f378 Add comment to README about making sure .local/bin is on PATH (#797)
To make it more clear how to set up the symlin on debian/ubuntu.

Fixes #791
2021-07-20 09:05:01 -06:00
Luca Poldelmengo ac4ded5b27 Add explanation for musl releases 2021-06-20 10:26:58 +02:00
Sudeshna Sur c9873b4b82 A few spell-checks is required in the readme file
fd is a program to find entries in your 'filesytem'. It must be filesystem instead :)
2021-05-25 06:35:46 +02:00
Kaspar 07ebce9419 Add rofi to integration with other programs in README.md
As discussed in ISSUE #753
2021-05-13 11:40:10 +02:00
Delapouite f60b7687a2 doc: add link to GNU parallel 2021-03-14 20:10:47 +01:00
Andrew Mitchell cb385a4822 Issue 624 newer older help text (#733)
* Document the newer and older aliases in the help text

* Documentation for new older aliases, remove commas

* Adds better clarification on alias usage for newer and older

* Fixes inconsistencies in help text for newer and older aliases
2021-02-25 07:39:44 +01:00
David Peter efc71bc00b Update CHANGELOG 2021-02-15 22:53:50 +01:00
Allen Wild 1a3615df9c set default path separator to '/' in MSYS
MSYS and MSYS2 environments (such as Git Bash) have a UNIX like
filesystem which uses '/' as the path separator rather than '\', but
Rust doesn't know about this by default.

On Windows, check the MSYSTEM environment variable and set the default
value of the --path-separator option to '/' for convenience.

There is no similar detection of Cygwin because there seems to be no way
for Rust (and any native Win32) programs to detect that they're being
called from a Cygwin environment. Cygwin users can use a shell
alias/function/script to wrap fd.

Fixes: https://github.com/sharkdp/fd/issues/537
2021-02-15 20:08:59 +01:00
David Peter cf7dd43f80 Hide --print0 from the short help text 2021-02-14 20:02:37 +01:00
David Peter 4ebb3bbc81 Explain what will be executed 2021-02-14 19:59:53 +01:00
David Peter d283aba52e Move command execution section 2021-02-14 19:51:10 +01:00
David Peter 5423c45660 Update command execution section 2021-02-14 19:50:50 +01:00
David Peter 98738d2ac1 Remove xargs section from How-to 2021-02-14 11:27:44 +01:00
David Peter 371794047a Update features section 2021-02-14 11:22:10 +01:00
David Peter c43c931985 Update intro 2021-02-14 11:17:04 +01:00
David Peter 41dc622fb5 Update features section 2021-02-14 11:07:44 +01:00
David Peter 266ba7893a Add quick links 2021-02-14 11:03:24 +01:00
David Peter 194dbe2033 Move benchmark and CLI sections 2021-02-14 10:57:09 +01:00
David Peter 97f1558daf Move colorized output section to 'Troubleshooting' 2021-02-14 10:55:30 +01:00
David Peter f00af363d1 Fix section heading levels 2021-02-14 10:52:53 +01:00
David Peter 2155d19be0 Change section header 2021-02-14 10:51:40 +01:00
David Peter 3e728f3edd Update LICENSE terms => 2021 2021-02-14 10:51:30 +01:00
David Peter 6645131234 Integrate regex link into text 2021-02-14 10:47:34 +01:00
Travis Stewart 189d6a907a Move and update Parallel command execution section 2021-02-14 10:45:26 +01:00
David Peter 8a83ad41c8 Remove alias suggestions 2021-02-14 10:44:52 +01:00
Chinmay 5feb89aa99 README update: Use soft link instead of alias for Debian.
As export command will not work with fd because fd is just an alias to
fdfind, create a soft link instead of alias.

Addresses https://github.com/sharkdp/fd/issues/728
2021-02-14 10:44:52 +01:00
Allen Wild 8a532e5196 use custom --path-separator in arguments to --exec
When --path-separator is used, pass it on to commands run with
--exec(-batch) and --list-details.

On Windows, paths with drive letters (C:\foo) and UNC paths
(\\server\share\path) are handled as expected, though they're unlikely
to be found in normal usage of fd.

Fixes: https://github.com/sharkdp/fd/issues/697
2021-02-14 10:24:41 +01:00
johnwdjiang b8089f8d58 hide private func 2021-02-13 21:55:38 +01:00
David Peter 85fb7389af Shorter help text for --exec 2021-02-13 21:25:50 +01:00
Archer Stéphane eb28030f87 include similar change to src/app.rs after feeback 2021-02-13 21:24:15 +01:00
Archer Stéphane 5d2e011e90 change documentation message after feedbacks 2021-02-13 21:24:15 +01:00
Archer Stéphane 64bde11887 Try to clarify the documentation for -x command
see issue #712
2021-02-13 21:24:15 +01:00
Richard Taityr b928af7d9c fix typo (#723) 2021-02-02 22:37:36 -07:00
Chathika Weerasuriya 9bc20e38b1 fixed: typographic error in help
- Changed "extention" to "extension"
2021-01-15 19:33:34 +01:00
sharkdp 6a3aae52fd Bump version to v8.2.1 2020-12-07 19:01:36 +01:00
Travis Stewart 7dc81381b0 Move Installation and Development below Tutorial (#701) 2020-12-07 06:54:00 +01:00
sharkdp c7cf2d1104 Revert using 'cleanup', we are still using dpl v1 2020-12-06 19:06:33 +01:00
sharkdp 91eaff056f Update CHANGELOG 2020-12-06 16:04:37 +01:00
sharkdp cadaef3f07 Show error if pattern matches leading dot but --hidden is not given, closes #615 2020-12-06 15:57:33 +01:00
sharkdp 17bd256ae6 Improve --fixed-strings help text, closes #627 2020-12-06 14:57:47 +01:00
sharkdp ecfd8cea41 Update --help text regarding pattern, closes #684 2020-12-06 12:31:31 +01:00
sharkdp d2659de782 Improve --extension help text, closes #673 2020-12-06 12:20:50 +01:00
sharkdp d205a7ff9e Improve --exclude help text, closes #661 2020-12-06 12:15:01 +01:00
sharkdp 296300ce46 Fix clippy suggestions 2020-12-06 12:04:47 +01:00
sharkdp ea48a96945 Run cargo fmt 2020-12-06 12:02:22 +01:00
sharkdp f2fec45099 Update dirs-next and users dependency 2020-12-06 12:02:04 +01:00
sharkdp 4faa201c89 Update dependencies 2020-12-06 12:00:11 +01:00
sharkdp 54603501ec Bump version 2020-12-06 11:56:36 +01:00
sharkdp 45c66e7f1b Update CHANGELOG 2020-12-06 11:54:54 +01:00
xanonid 441a235a0f Simplify code a bit (suggestions by clippy) 2020-12-06 11:52:10 +01:00
xanonid 2cd3de8b94 Use NO_COLOR env variable check also for help screen 2020-12-06 11:52:10 +01:00
Marco Sirabella 6830b3dee9 Better defaults for LsColors 2020-12-06 11:48:19 +01:00
Platon Pronko f3edaf0e4d add changelog entry for exact file sizes in --size filter 2020-12-06 11:47:27 +01:00
Platon Pronko e97a1e90cb Add exact size support to the --size filter (#669) 2020-12-06 11:47:27 +01:00
Andreas Lappe 2338b3bc81 Fix typo in fd.1 2020-12-04 12:04:19 +01:00
sharkdp 9412648ee9 Add link to troubleshooting section 2020-12-01 19:44:13 +01:00
sharkdp 0a22dcc044 Add CHANGELOG section to CONTRIBUTING.md 2020-12-01 19:38:05 +01:00
Nick Flueckiger 24402dda7c Remove .UE and .UR to fix url display on macos 2020-11-06 21:44:21 +01:00
Nicholas Bailey 4f8697c407 Update README to have updated instructions. 2020-10-30 08:09:06 +01:00
sharkdp b2fa188029 Avoid panic when sending errors after receiver shutdown
fixes #678
2020-10-28 13:10:25 +01:00
David Peter e0adb45d08 Fail with error message if numeric arguments can not be parsed
closes #675
2020-10-25 21:18:53 +01:00
Matthias Reitinger ec4cc981fc Add --prune flag
closes #535
2020-10-25 08:16:01 +01:00
Amrit Rathie 06eb231fbd Update lockfile 2020-10-24 15:25:07 +02:00
Amrit Rathie fd1ddc9d08 Change dependency dirs to dirs-next 2020-10-24 15:25:07 +02:00
Matthias Reitinger e852009876 Add NO_COLOR to the man page 2020-10-24 12:15:06 +02:00
Allen Wild 99934ab98c add --base-directory, --path-separator, and --search-path to the man page
These options are in `fd --help` but missing from the man page.
Also fix a typo in the help text for --base-directory.
2020-10-14 08:35:09 +02:00
Craig Hills 633d486ac1 Adjust the regex syntax reference wording 2020-10-14 08:34:06 +02:00
Craig Hills 4d2472bfab Add documentation around regex syntax
This is to address https://github.com/sharkdp/fd/issues/622
2020-10-14 08:34:06 +02:00
Fredrik Fornwall a851570b15 Disable jemalloc on Android (fixes #636 and #642)
Disabling jemalloc for Android fixes a build failure since jemalloc does
not build out of the box for that platform on older API versions.

Android started using jemalloc as the system allocator around 2014 for
most devices. In the latest Android version (11) the system allocator
has been switched to Scudo, which while not being jemalloc is modern and
actively maintained and should be used for most software on Android.

See:
- https://android-developers.googleblog.com/2020/06/system-hardening-in-android-11.html
- https://blog.nsogroup.com/a-tale-of-two-mallocs-on-android-libc-allocators-part-2-jemalloc/
- https://stackoverflow.com/questions/2266762/android-libc-version-and-malloc-implementation
2020-10-08 12:45:48 +02:00
sharkdp f064e41bf1 Update CHANGELOG 2020-10-05 23:30:00 +02:00
Sebastián Mancilla ef1bfc7508 Add native Zsh completion
Replace the auto-generated Zsh completion with a full completion script.
This script improves completion support for options and arguments,
which is hard to obtain from an auto-generated completion.

- Full completion support for external commands and options when using
  `--exec`/`--exec-batch`.

- The completion for `--type` and `--color` adds descriptions instead of
  just values.

- Complete files when using `--ignore-file`.

- Stop completing files for the pattern argument.

- Improve completion for path arguments. Only directories are
  completed.

- Single-use options are not offered again.

- Updated exclusions lists for mutual-exclusive options.

- Add support for hidden options (as defined in `app.rs`). They are not
  offered for completion, but if present are considered (and their value
  may be completed).

Full support for exclusion lists depends of options groups, which was
added in Zsh 5.4. Older Zsh versions, as commented in the completion
script, will just offer most options.

The format of the completion script was taken from ripgrep completion
script. Command completion for `--exec`/`--exec-batch` was taken from
Zsh's `_find` completion.
2020-10-05 22:39:17 +02:00
Matthias Reitinger a38da63a34 Replace aliases in Travis config 2020-10-04 21:50:49 +02:00
Matthias Reitinger 9a49d0d6d0 Fix deprecated Travis config keys
* root: deprecated key sudo (The key `sudo` has no effect anymore.)
* deploy: deprecated key skip_cleanup (not supported in dpl v2, use
  cleanup)
2020-10-04 21:50:49 +02:00
gorogoroumaru 74c3431a2b improve time option usage (#645)
* improve time option usage
2020-09-10 00:34:27 -06:00
yuuji.yaginuma 1b4e1e9c1a Fix a link to ubuntu official page
Currently, https://packages.ubuntu.com/disco/fd-find shows an error.
This is because of the package page that unsupported version was removed.

So I removed the version from the link. That link shows a search result of `fd-find`
and can find OSs that can install `fd-find`.
2020-08-08 07:46:45 +02:00
David Peter 6f2c8cdf91 Add tmccombs as maintainer 2020-06-10 07:42:49 +02:00
David Peter e2f90dba44 Update LS_COLORS help text 2020-05-25 16:47:51 +02:00
sharkdp 5648597a61 Bump version to 8.1.1 2020-05-25 15:38:30 +02:00
sharkdp b3258e00df Update CHANGELOG 2020-05-25 15:37:03 +02:00
sharkdp 41d7cc33ca Add CHANGELOG entry 2020-05-25 12:17:32 +02:00
sharkdp a81fef9992 Windows: Check for xterm-256color environment variable as a proxy for color support 2020-05-25 12:17:32 +02:00
sharkdp 53c338d71f Windows: Always enable colors if --color=always is set 2020-05-25 12:17:32 +02:00
Dmitry Marakasov d77daa89a2 Merged into single project in repology 2020-05-24 17:25:52 +02:00
33 changed files with 2186 additions and 1250 deletions
+5
View File
@@ -9,6 +9,11 @@ assignees: ''
**Describe the bug you encountered:**
<!--
Please check out the troubleshooting section first:
https://github.com/sharkdp/fd#troubleshooting
-->
**Describe what you expected to happen:**
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "monthly"
+333
View File
@@ -0,0 +1,333 @@
name: CICD
env:
MIN_SUPPORTED_RUST_VERSION: "1.42.0"
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
on:
workflow_dispatch:
pull_request:
push:
branches:
- master
tags:
- '*'
jobs:
min_version:
name: Minimum supported rust version
runs-on: ubuntu-18.04
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Install rust toolchain (v${{ env.MIN_SUPPORTED_RUST_VERSION }})
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: clippy
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --all-features
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
build:
name: ${{ matrix.job.os }} (${{ matrix.job.target }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-18.04, target: arm-unknown-linux-gnueabihf , use-cross: true }
- { os: ubuntu-18.04, target: arm-unknown-linux-musleabihf, use-cross: true }
- { os: ubuntu-18.04, target: aarch64-unknown-linux-gnu , use-cross: true }
- { os: ubuntu-18.04, target: i686-unknown-linux-gnu , use-cross: true }
- { os: ubuntu-18.04, target: i686-unknown-linux-musl , use-cross: true }
- { os: ubuntu-18.04, target: x86_64-unknown-linux-gnu }
- { os: ubuntu-18.04, target: x86_64-unknown-linux-musl , use-cross: true }
- { os: macos-10.15 , target: x86_64-apple-darwin }
# - { os: windows-2019, target: i686-pc-windows-gnu } ## disabled; error: linker `i686-w64-mingw32-gcc` not found
- { os: windows-2019, target: i686-pc-windows-msvc }
- { os: windows-2019, target: x86_64-pc-windows-gnu }
- { os: windows-2019, target: x86_64-pc-windows-msvc }
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Install prerequisites
shell: bash
run: |
case ${{ matrix.job.target }} in
arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
esac
- name: Extract crate information
shell: bash
run: |
echo "PROJECT_NAME=fd" >> $GITHUB_ENV
echo "PROJECT_VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)" >> $GITHUB_ENV
echo "PROJECT_MAINTAINER=$(sed -n 's/^authors = \["\(.*\)"\]/\1/p' Cargo.toml)" >> $GITHUB_ENV
echo "PROJECT_HOMEPAGE=$(sed -n 's/^homepage = "\(.*\)"/\1/p' Cargo.toml)" >> $GITHUB_ENV
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- name: Show version information (Rust, cargo, GCC)
shell: bash
run: |
gcc --version || true
rustup -V
rustup toolchain list
rustup default
cargo -V
rustc -V
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: build
args: --release --target=${{ matrix.job.target }}
- name: Strip debug information from executable
id: strip
shell: bash
run: |
# Figure out suffix of binary
EXE_suffix=""
case ${{ matrix.job.target }} in
*-pc-windows-*) EXE_suffix=".exe" ;;
esac;
# Figure out what strip tool to use if any
STRIP="strip"
case ${{ matrix.job.target }} in
arm-unknown-linux-*) STRIP="arm-linux-gnueabihf-strip" ;;
aarch64-unknown-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;;
*-pc-windows-msvc) STRIP="" ;;
esac;
# Setup paths
BIN_DIR="${{ env.CICD_INTERMEDIATES_DIR }}/stripped-release-bin/"
mkdir -p "${BIN_DIR}"
BIN_NAME="${{ env.PROJECT_NAME }}${EXE_suffix}"
BIN_PATH="${BIN_DIR}/${BIN_NAME}"
# Copy the release build binary to the result location
cp "target/${{ matrix.job.target }}/release/${BIN_NAME}" "${BIN_DIR}"
# Also strip if possible
if [ -n "${STRIP}" ]; then
"${STRIP}" "${BIN_PATH}"
fi
# Let subsequent steps know where to find the (stripped) bin
echo ::set-output name=BIN_PATH::${BIN_PATH}
echo ::set-output name=BIN_NAME::${BIN_NAME}
- name: Set testing options
id: test-options
shell: bash
run: |
unset CARGO_TEST_OPTIONS
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--bin ${PROJECT_NAME}" ;; esac;
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
- name: Run tests
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: test
args: --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
- name: Create tarball
id: package
shell: bash
run: |
PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
PKG_BASENAME=${PROJECT_NAME}-v${PROJECT_VERSION}-${{ matrix.job.target }}
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
echo ::set-output name=PKG_NAME::${PKG_NAME}
PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package"
ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/"
mkdir -p "${ARCHIVE_DIR}"
mkdir -p "${ARCHIVE_DIR}/autocomplete"
# Binary
cp "${{ steps.strip.outputs.BIN_PATH }}" "$ARCHIVE_DIR"
# Man page
cp 'doc/${{ env.PROJECT_NAME }}.1' "$ARCHIVE_DIR"
# README, LICENSE and CHANGELOG files
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
# Autocompletion files
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'${{ env.PROJECT_NAME }}.bash' "$ARCHIVE_DIR/autocomplete/"
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'${{ env.PROJECT_NAME }}.fish' "$ARCHIVE_DIR/autocomplete/"
cp 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'_${{ env.PROJECT_NAME }}.ps1' "$ARCHIVE_DIR/autocomplete/"
cp 'contrib/completion/_fd' "$ARCHIVE_DIR/autocomplete/"
# base compressed package
pushd "${PKG_STAGING}/" >/dev/null
case ${{ matrix.job.target }} in
*-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;;
*) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;;
esac;
popd >/dev/null
# Let subsequent steps know where to find the compressed package
echo ::set-output name=PKG_PATH::"${PKG_STAGING}/${PKG_NAME}"
- name: Create Debian package
id: debian-package
shell: bash
if: startsWith(matrix.job.os, 'ubuntu')
run: |
COPYRIGHT_YEARS="2018 - "$(date "+%Y")
DPKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/debian-package"
DPKG_DIR="${DPKG_STAGING}/dpkg"
mkdir -p "${DPKG_DIR}"
DPKG_BASENAME=${PROJECT_NAME}
DPKG_CONFLICTS=${PROJECT_NAME}-musl
case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${PROJECT_NAME}-musl ; DPKG_CONFLICTS=${PROJECT_NAME} ;; esac;
DPKG_VERSION=${PROJECT_VERSION}
unset DPKG_ARCH
case ${{ matrix.job.target }} in
aarch64-*-linux-*) DPKG_ARCH=arm64 ;;
arm-*-linux-*hf) DPKG_ARCH=armhf ;;
i686-*-linux-*) DPKG_ARCH=i686 ;;
x86_64-*-linux-*) DPKG_ARCH=amd64 ;;
*) DPKG_ARCH=notset ;;
esac;
DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb"
echo ::set-output name=DPKG_NAME::${DPKG_NAME}
# Binary
install -Dm755 "${{ steps.strip.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.strip.outputs.BIN_NAME }}"
# Man page
install -Dm644 'doc/${{ env.PROJECT_NAME }}.1' "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1"
gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1"
# Autocompletion files
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'${{ env.PROJECT_NAME }}.bash' "${DPKG_DIR}/usr/share/bash-completion/completions/${{ env.PROJECT_NAME }}"
install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ env.PROJECT_NAME }}'*/out/'${{ env.PROJECT_NAME }}.fish' "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ env.PROJECT_NAME }}.fish"
install -Dm644 'contrib/completion/_fd' "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ env.PROJECT_NAME }}"
# README and LICENSE
install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md"
install -Dm644 "LICENSE-MIT" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-MIT"
install -Dm644 "LICENSE-APACHE" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-APACHE"
install -Dm644 "CHANGELOG.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog"
gzip -n --best "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog"
cat > "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright" <<EOF
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: ${{ env.PROJECT_NAME }}
Source: ${{ env.PROJECT_HOMEPAGE }}
Files: *
Copyright: ${{ env.PROJECT_MAINTAINER }}
Copyright: $COPYRIGHT_YEARS ${{ env.PROJECT_MAINTAINER }}
License: Apache-2.0 or MIT
License: Apache-2.0
On Debian systems, the complete text of the Apache-2.0 can be found in the
file /usr/share/common-licenses/Apache-2.0.
License: MIT
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
.
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
EOF
chmod 644 "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright"
# control file
mkdir -p "${DPKG_DIR}/DEBIAN"
cat > "${DPKG_DIR}/DEBIAN/control" <<EOF
Package: ${DPKG_BASENAME}
Version: ${DPKG_VERSION}
Section: utils
Priority: optional
Maintainer: ${{ env.PROJECT_MAINTAINER }}
Homepage: ${{ env.PROJECT_HOMEPAGE }}
Architecture: ${DPKG_ARCH}
Provides: ${{ env.PROJECT_NAME }}
Conflicts: ${DPKG_CONFLICTS}
Description: cat(1) clone with wings.
A cat(1) clone with syntax highlighting and Git integration.
EOF
DPKG_PATH="${DPKG_STAGING}/${DPKG_NAME}"
echo ::set-output name=DPKG_PATH::${DPKG_PATH}
# build dpkg
fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}"
- name: "Artifact upload: tarball"
uses: actions/upload-artifact@master
with:
name: ${{ steps.package.outputs.PKG_NAME }}
path: ${{ steps.package.outputs.PKG_PATH }}
- name: "Artifact upload: Debian package"
uses: actions/upload-artifact@master
if: steps.debian-package.outputs.DPKG_NAME
with:
name: ${{ steps.debian-package.outputs.DPKG_NAME }}
path: ${{ steps.debian-package.outputs.DPKG_PATH }}
- name: Check for release
id: is-release
shell: bash
run: |
unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi
echo ::set-output name=IS_RELEASE::${IS_RELEASE}
- name: Publish archives and packages
uses: softprops/action-gh-release@v1
if: steps.is-release.outputs.IS_RELEASE
with:
files: |
${{ steps.package.outputs.PKG_PATH }}
${{ steps.debian-package.outputs.DPKG_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-106
View File
@@ -1,106 +0,0 @@
language: rust
matrix:
include:
# Stable channel.
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-gnu
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-musl
- os: linux
rust: stable
env: TARGET=i686-unknown-linux-gnu
- os: linux
rust: stable
env: TARGET=i686-unknown-linux-musl
- os: osx
osx_image: xcode11.4 # Catalina
rust: stable
env: TARGET=x86_64-apple-darwin
- os: linux
rust: stable
env:
- TARGET=arm-unknown-linux-gnueabihf
- CC_arm_unknown_linux_gnueabihf=/usr/bin/arm-linux-gnueabihf-gcc-4.8
- CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc-4.8
- os: linux
rust: stable
env:
- TARGET=arm-unknown-linux-musleabihf
- CARGO_TARGET_ARM_UNKNOWN_LINUX_MUSLEABIHF_LINKER=arm-linux-gnueabihf-gcc-4.8
# Beta channel.
- os: linux
rust: beta
env: TARGET=x86_64-unknown-linux-gnu
# Minimum Rust supported channel.
- os: linux
rust: 1.36.0
env: TARGET=x86_64-unknown-linux-gnu
- os: linux
rust: 1.36.0
env: TARGET=x86_64-unknown-linux-musl
- os: linux
rust: 1.36.0
env: TARGET=i686-unknown-linux-gnu
- os: linux
rust: 1.36.0
env: TARGET=i686-unknown-linux-musl
- os: osx
rust: 1.36.0
env: TARGET=x86_64-apple-darwin
sudo: required
before_install:
- ci/before_install.bash
env:
global:
# Default target on travis-ci.
# Used as conditional check in the install stage
- HOST=x86_64-unknown-linux-gnu
# Used on the deployment script
- PROJECT_NAME=fd
install:
# prevent target re-add error from rustup
- if [[ $TRAVIS_OS_NAME = linux && $HOST != $TARGET ]]; then rustup target add $TARGET; fi
script:
- ci/script.bash
before_deploy:
- bash ci/before_deploy.bash
deploy:
provider: releases
# NOTE updating the `api_key.secure`
# - go to: https://github.com/settings/tokens/new
# - generate new token using `public_repo` scope
# - encrypt it using: `travis encrypt API_KEY_HERE`
# - paste the output below
api_key:
secure: "RyFdh2lpDmaNhPar7ezsb18Xz+6XFM40y7cZCDRML+Sk+eYK1xtDNfEhDRJU5Qo1ReVsByds/QJTSXr2KmZPk3lXwG3SiN7UtrLUxCxFr6qrcM/iujlKTf5UxeRklkzPXxnH95DEyEgxvgbVhWTGVDWoyMnrVQXZKDy6z1iAiYB5h2Zl1rs+MRb/Enlt5q6XIKAlG0ppGtl8CfYudq5ZiqfJaMWTt9SWm2YskC8FeMc0S3IM6/EhTvaNYLdaarFqVWQEVql+6oCuL3ayPzmGyxLdxM37tIMNQ0f97zxqWodacXTG5ULdRD8if1l/SmTujrtjbZ0KWRjsjOq4vBtxBJKGdprcSiB0xH/hToqqtTSO0z5FPXi5cB8UlK6YLDDHcP3kXNer8CYMLI1VPaUDLTF57/0/RPi2DZiiGfZsIAS6PsICbHdTQVzxQckM4lN1vnAGgkhXIMbztml21pv+QrGy98OZJ0ubf5ztgQhpT0WPH4JXT8M6htsoo8dZf8lQ5aLfmW9RKePJDqixQwPqmimPIkrlxRDTDGII0ZAZws7l779eOLmEcM2tH2HbsUKUCZIG/pRHLSlP45Jn2bULGzuXZ2daq70z6zvIbom0CUzSXIvdTXEZI2AM5RBvPYGGaKI8YlxgRdQvJp3h0BzPdFOXI3RAxscCY7PJpa/RdIg="
# for uploading multiple files
file_glob: true
# NOTE explanation on each env variable
# - PROJECT_NAME: name of the project, set on the `env.global` above
# - TRAVIS_TAG: tag name that the build is being deployed for, usually the version number
# - TARGET: target triple of the build
file:
- $PROJECT_NAME-$TRAVIS_TAG-$TARGET.*
- $PROJECT_NAME*.deb
# don't delete artifacts from previous stage
skip_cleanup: true
on:
# deploy only if we push a tag
tags: true
# deploy only on stable channel that has TARGET env variable sets
condition: $TRAVIS_RUST_VERSION = stable && $TARGET != ""
notifications:
email:
on_success: never
+48
View File
@@ -1,10 +1,58 @@
# Upcoming release
## Features
## Bugfixes
- Set default path separator to `/` in MSYS, see #537 and #730 (@aswild)
- fd cannot search files under a RAM disk, see #752
- fd doesn't show substituted drive on Windows, see #365
- Properly handle write errors to devices that are full, see #737
## Changes
- Apply custom `--path-separator` to commands run with `--exec(-batch)` and `--list-details`, see #697 (@aswild)
## Other
- Many documentation updates
# v8.2.1
No functional changes with respect to v8.2.0. Bugfix in the release process.
# v8.2.0
## Features
- Add new `--prune` flag, see #535 (@reima)
- Improved the usability of the time-based options, see #624 and #645 (@gorogoroumaru)
- Add support for exact file sizes in the `--size` filter, see #669 and #696 (@Rogach)
- `fd` now prints an error message if the search pattern requires a leading dot but
`--hidden` is not enabled (Unix only), see #615
## Bugfixes
- Avoid panic when performing limited searches in directories with restricted permissions, see #678
- Invalid numeric command-line arguments are silently ignored, see #675
- Disable jemalloc on Android, see #662
- The `--help` text will be colorless if `NO_COLOR` has been set, see #600 (@xanonid)
## Changes
- If `LS_COLORS` is not set (e.g. on Windows), we now provide a more comprehensive default which
includes much more filetypes, see #604 and #682 (mjsir911).
## Other
- Added `zsh` completion files, see #654 and #189 (@smancill)
# v8.1.1
## Bugfixes
- Support colored output on older Windows versions if either (1) `--color=always` is set or (2) the `TERM` environment variable is set. See #469
# v8.1.0
## Features
+18
View File
@@ -10,6 +10,24 @@ We welcome any form of contribution:
**Note**: Before you take the time to open a pull request, please open a ticket first. This will
give us the chance to discuss any potential changes first.
## Add an entry to the changelog
If your contribution changes the behavior of `fd` (as opposed to a typo-fix
in the documentation), please update the [`CHANGELOG.md`](CHANGELOG.md) file
and describe your changes. This makes the release process much easier and
therefore helps to get your changes into a new `fd` release faster.
The top of the `CHANGELOG` contains a *"unreleased"* section with a few
subsections (Features, Bugfixes, …). Please add your entry to the subsection
that best describes your change.
Entries follow this format:
```
- Short description of what has been changed, see #123 (@user)
```
Here, `#123` is the number of the original issue and/or your pull request.
Please replace `@user` by your GitHub username.
## Important links
* [Open issues](https://github.com/sharkdp/fd/issues)
Generated
+231 -263
View File
@@ -1,603 +1,571 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.10"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "arrayvec"
version = "0.5.1"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "base64"
version = "0.11.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "blake2b_simd"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bstr"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf"
dependencies = [
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr",
]
[[package]]
name = "cc"
version = "1.0.53"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.1"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.11.0",
"atty",
"bitflags",
"strsim",
"term_size",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg",
"cfg-if 1.0.0",
"lazy_static",
]
[[package]]
name = "ctrlc"
version = "3.1.4"
version = "3.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "232295399409a8b7ae41276757b5a1cc21032848d42bff2352261f958b3ca29a"
dependencies = [
"nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"nix",
"winapi",
]
[[package]]
name = "diff"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
[[package]]
name = "dirs"
version = "2.0.2"
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 1.0.0",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys"
version = "0.3.4"
name = "dirs-sys-next"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "fd-find"
version = "8.1.0"
version = "8.2.1"
dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"diff 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"filetime 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"humantime 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ignore 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
"jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"lscolors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"users 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.12.1",
"anyhow",
"atty",
"clap",
"ctrlc",
"diff",
"dirs-next",
"filetime",
"globset",
"humantime",
"ignore",
"jemallocator",
"lazy_static",
"libc",
"lscolors",
"normpath",
"num_cpus",
"regex",
"regex-syntax",
"tempdir",
"users",
"version_check",
]
[[package]]
name = "filetime"
version = "0.2.10"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.2.10",
"winapi",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fs_extra"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "getrandom"
version = "0.1.14"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10",
"libc",
"wasi",
]
[[package]]
name = "globset"
version = "0.4.5"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd"
dependencies = [
"aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
"bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"aho-corasick",
"bstr",
"fnv",
"log",
"regex",
]
[[package]]
name = "hermit-abi"
version = "0.1.13"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
dependencies = [
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
]
[[package]]
name = "humantime"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
[[package]]
name = "ignore"
version = "0.4.15"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c"
dependencies = [
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils",
"globset",
"lazy_static",
"log",
"memchr",
"regex",
"same-file",
"thread_local",
"walkdir",
"winapi-util",
]
[[package]]
name = "jemalloc-sys"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45"
dependencies = [
"cc 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
"fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"cc",
"fs_extra",
"libc",
]
[[package]]
name = "jemallocator"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69"
dependencies = [
"jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"jemalloc-sys",
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.70"
version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "log"
version = "0.4.8"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10",
]
[[package]]
name = "lscolors"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24b894c45c9da468621cdd615a5a79ee5e5523dd4f75c76ebc03d458940c16e"
dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.12.1",
]
[[package]]
name = "memchr"
version = "2.3.3"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "nix"
version = "0.17.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags",
"cc",
"cfg-if 1.0.0",
"libc",
]
[[package]]
name = "normpath"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27e6e8f70e9fbbe3752d330d769e3424f24b9458ce266df93a3b456902fd696a"
dependencies = [
"winapi",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi",
"libc",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"getrandom",
"redox_syscall 0.1.57",
]
[[package]]
name = "regex"
version = "1.3.7"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.17"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "remove_dir_all"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-argon2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand",
"remove_dir_all",
]
[[package]]
name = "term_size"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
dependencies = [
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"term_size 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size",
"unicode-width",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static",
]
[[package]]
name = "unicode-width"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "users"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
dependencies = [
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
"log",
]
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "walkdir"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
dependencies = [
"same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "winapi"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
"checksum bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
"checksum cc 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
"checksum ctrlc 3.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7a4ba686dff9fa4c1c9636ce1010b0cf98ceb421361b0bb3d6faeec43bd217a7"
"checksum diff 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
"checksum filetime 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695"
"checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
"checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120"
"checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
"checksum humantime 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9b6c53306532d3c8e8087b44e6580e10db51a023cf9b433cea2ac38066b92da"
"checksum ignore 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "128b9e89d15a3faa642ee164c998fd4fae3d89d054463cddb2c25a7baad3a352"
"checksum jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45"
"checksum jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum lscolors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f77452267149eac960ded529fe5f5460ddf792845a1d71b5d0cfcee5642e47e"
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
"checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
"checksum regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
"checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
"checksum term_size 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum users 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486"
"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+7 -6
View File
@@ -16,7 +16,7 @@ license = "MIT/Apache-2.0"
name = "fd-find"
readme = "README.md"
repository = "https://github.com/sharkdp/fd"
version = "8.1.0"
version = "8.2.1"
edition= "2018"
[badges.appveyor]
@@ -39,21 +39,22 @@ atty = "0.2"
ignore = "0.4.3"
lazy_static = "1.1.0"
num_cpus = "1.8"
regex = "1.0.0"
regex = "1.5.4"
regex-syntax = "0.6"
ctrlc = "3.1"
humantime = "2.0"
lscolors = "0.7"
globset = "0.4"
anyhow = "1.0"
dirs = "2.0"
dirs-next = "2.0"
normpath = "0.3"
[dependencies.clap]
version = "2.31.2"
features = ["suggestions", "color", "wrap_help"]
[target.'cfg(unix)'.dependencies]
users = "0.10.0"
users = "0.11.0"
[target.'cfg(all(unix, not(target_os = "redox")))'.dependencies]
libc = "0.2"
@@ -61,13 +62,13 @@ libc = "0.2"
# FIXME: Re-enable jemalloc on macOS
# jemalloc is currently disabled on macOS due to a bug in jemalloc in combination with macOS
# Catalina. See https://github.com/sharkdp/fd/issues/498 for details.
[target.'cfg(all(not(windows), not(target_os = "macos"), not(target_env = "musl")))'.dependencies]
[target.'cfg(all(not(windows), not(target_os = "android"), not(target_os = "macos"), not(target_env = "musl")))'.dependencies]
jemallocator = "0.3.0"
[dev-dependencies]
diff = "0.1"
tempdir = "0.3"
filetime = "0.2.1"
filetime = "0.2.14"
[profile.release]
lto = true
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017-2020 The fd developers
Copyright (c) 2017-2021 The fd developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+413 -345
View File
@@ -1,35 +1,305 @@
# fd
[![Build Status](https://travis-ci.org/sharkdp/fd.svg?branch=master)](https://travis-ci.org/sharkdp/fd)
[![Build status](https://ci.appveyor.com/api/projects/status/21c4p5fwggc5gy3j/branch/master?svg=true)](https://ci.appveyor.com/project/sharkdp/fd/branch/master)
[![CICD](https://github.com/sharkdp/fd/actions/workflows/CICD.yml/badge.svg)](https://github.com/sharkdp/fd/actions/workflows/CICD.yml)
[![Version info](https://img.shields.io/crates/v/fd-find.svg)](https://crates.io/crates/fd-find)
[中文](https://github.com/chinanf-boy/fd-zh)
[한국어](https://github.com/spearkkk/fd-kor)
[[中文](https://github.com/chinanf-boy/fd-zh)]
[[한국어](https://github.com/spearkkk/fd-kor)]
*fd* is a simple, fast and user-friendly alternative to
[*find*](https://www.gnu.org/software/findutils/).
`fd` is a program to find entries in your filesystem.
It is a simple, fast and user-friendly alternative to [`find`](https://www.gnu.org/software/findutils/).
While it does not aim to support all of `find`'s powerful functionality, it provides sensible
(opinionated) defaults for a majority of use cases.
While it does not seek to mirror all of *find*'s powerful functionality, it provides sensible
(opinionated) defaults for [80%](https://en.wikipedia.org/wiki/Pareto_principle) of the use cases.
Quick links:
* [How to use](#how-to-use)
* [Installation](#installation)
* [Troubleshooting](#troubleshooting)
## Features
* Convenient syntax: `fd PATTERN` instead of `find -iname '*PATTERN*'`.
* Colorized terminal output (similar to *ls*).
* It's *fast* (see [benchmarks](#benchmark) below).
* Intuitive syntax: `fd PATTERN` instead of `find -iname '*PATTERN*'`.
* Regular expression (default) and glob-based patterns.
* [Very fast](#benchmark) due to parallelized directory traversal.
* Uses colors to highlight different file types (same as *ls*).
* Supports [parallel command execution](#command-execution)
* Smart case: the search is case-insensitive by default. It switches to
case-sensitive if the pattern contains an uppercase
character[\*](http://vimdoc.sourceforge.net/htmldoc/options.html#'smartcase').
* Ignores hidden directories and files, by default.
* Ignores patterns from your `.gitignore`, by default.
* Regular expressions.
* Unicode-awareness.
* The command name is *50%* shorter[\*](https://github.com/ggreer/the_silver_searcher) than
`find` :-).
* Parallel command execution with a syntax similar to GNU Parallel.
## Demo
![Demo](doc/screencast.svg)
## How to use
First, to get an overview of all available command line options, you can either run
[`fd -h`](#command-line-options) for a concise help message or `fd --help` for a more detailed
version.
### Simple search
*fd* is designed to find entries in your filesystem. The most basic search you can perform is to
run *fd* with a single argument: the search pattern. For example, assume that you want to find an
old script of yours (the name included `netflix`):
``` bash
> fd netfl
Software/python/imdb-ratings/netflix-details.py
```
If called with just a single argument like this, *fd* searches the current directory recursively
for any entries that *contain* the pattern `netfl`.
### Regular expression search
The search pattern is treated as a regular expression. Here, we search for entries that start
with `x` and end with `rc`:
``` bash
> cd /etc
> fd '^x.*rc$'
X11/xinit/xinitrc
X11/xinit/xserverrc
```
The regular expression syntax used by `fd` is [documented here](https://docs.rs/regex/1.0.0/regex/#syntax).
### Specifying the root directory
If we want to search a specific directory, it can be given as a second argument to *fd*:
``` bash
> fd passwd /etc
/etc/default/passwd
/etc/pam.d/passwd
/etc/passwd
```
### List all files, recursively
*fd* can be called with no arguments. This is very useful to get a quick overview of all entries
in the current directory, recursively (similar to `ls -R`):
``` bash
> cd fd/tests
> fd
testenv
testenv/mod.rs
tests.rs
```
If you want to use this functionality to list all files in a given directory, you have to use
a catch-all pattern such as `.` or `^`:
``` bash
> fd . fd/tests/
testenv
testenv/mod.rs
tests.rs
```
### Searching for a particular file extension
Often, we are interested in all files of a particular type. This can be done with the `-e` (or
`--extension`) option. Here, we search for all Markdown files in the fd repository:
``` bash
> cd fd
> fd -e md
CONTRIBUTING.md
README.md
```
The `-e` option can be used in combination with a search pattern:
``` bash
> fd -e rs mod
src/fshelper/mod.rs
src/lscolors/mod.rs
tests/testenv/mod.rs
```
### Hidden and ignored files
By default, *fd* does not search hidden directories and does not show hidden files in the
search results. To disable this behavior, we can use the `-H` (or `--hidden`) option:
``` bash
> fd pre-commit
> fd -H pre-commit
.git/hooks/pre-commit.sample
```
If we work in a directory that is a Git repository (or includes Git repositories), *fd* does not
search folders (and does not show files) that match one of the `.gitignore` patterns. To disable
this behavior, we can use the `-I` (or `--no-ignore`) option:
``` bash
> fd num_cpu
> fd -I num_cpu
target/debug/deps/libnum_cpus-f5ce7ef99006aa05.rlib
```
To really search *all* files and directories, simply combine the hidden and ignore features to show
everything (`-HI`).
### Command execution
Instead of just showing the search results, you often want to *do something* with them. `fd`
provides two ways to execute external commands for each of your search results:
* The `-x`/`--exec` option runs an external command *for each of the search results* (in parallel).
* The `-X`/`--exec-batch` option launches the external command once, with *all search results as arguments*.
#### Examples
Recursively find all zip archives and unpack them:
``` bash
fd -e zip -x unzip
```
If there are two such files, `file1.zip` and `backup/file2.zip`, this would execute
`unzip file1.zip` and `unzip backup/file2.zip`. The two `unzip` processes run in parallel
(if the files are found fast enough).
Find all `*.h` and `*.cpp` files and auto-format them inplace with `clang-format -i`:
``` bash
fd -e h -e cpp -x clang-format -i
```
Note how the `-i` option to `clang-format` can be passed as a separate argument. This is why
we put the `-x` option last.
Find all `test_*.py` files and open them in your favorite editor:
``` bash
fd -g 'test_*.py' -X vim
```
Note that we use capital `-X` here to open a single `vim` instance. If there are two such files,
`test_basic.py` and `lib/test_advanced.py`, this will run `vim test_basic.py lib/test_advanced.py`.
To see details like file permissions, owners, file sizes etc., you can tell `fd` to show them
by running `ls` for each result:
``` bash
fd … -X ls -lhd --color=always
```
This pattern is so useful that `fd` provides a shortcut. You can use the `-l`/`--list-details`
option to execute `ls` in this way: `fd … -l`.
Convert all `*.jpg` files to `*.png` files:
``` bash
fd -e jpg -x convert {} {.}.png
```
Here, `{}` is a placeholder for the search result. `{.}` is the same, without the file extension.
See below for more details on the placeholder syntax.
#### Placeholder syntax
The `-x` and `-X` options take a *command template* as a series of arguments (instead of a single string).
If you want to add additional options to `fd` after the command template, you can terminate it with a `\;`.
The syntax for generating commands is similar to that of [GNU Parallel](https://www.gnu.org/software/parallel/):
- `{}`: A placeholder token that will be replaced with the path of the search result
(`documents/images/party.jpg`).
- `{.}`: Like `{}`, but without the file extension (`documents/images/party`).
- `{/}`: A placeholder that will be replaced by the basename of the search result (`party.jpg`).
- `{//}`: The parent of the discovered path (`documents/images`).
- `{/.}`: The basename, with the extension removed (`party`).
If you do not include a placeholder, *fd* automatically adds a `{}` at the end.
#### Parallel vs. serial execution
For `-x`/`--exec`, you can control the number of parallel jobs by using the `-j`/`--threads` option.
Use `--threads=1` for serial execution.
### Excluding specific files or directories
Sometimes we want to ignore search results from a specific subdirectory. For example, we might
want to search all hidden files and directories (`-H`) but exclude all matches from `.git`
directories. We can use the `-E` (or `--exclude`) option for this. It takes an arbitrary glob
pattern as an argument:
``` bash
> fd -H -E .git …
```
We can also use this to skip mounted directories:
``` bash
> fd -E /mnt/external-drive …
```
.. or to skip certain file types:
``` bash
> fd -E '*.bak' …
```
To make exclude-patterns like these permanent, you can create a `.fdignore` file. They work like
`.gitignore` files, but are specific to `fd`. For example:
``` bash
> cat ~/.fdignore
/mnt/external-drive
*.bak
```
Note: `fd` also supports `.ignore` files that are used by other programs such as `rg` or `ag`.
If you want `fd` to ignore these patterns globally, you can put them in `fd`'s global ignore file.
This is usually located in `~/.config/fd/ignore` in macOS or Linux, and `%APPDATA%\fd\ignore` in
Windows.
### Deleting files
You can use `fd` to remove all files and directories that are matched by your search pattern.
If you only want to remove files, you can use the `--exec-batch`/`-X` option to call `rm`. For
example, to recursively remove all `.DS_Store` files, run:
``` bash
> fd -H '^\.DS_Store$' -tf -X rm
```
If you are unsure, always call `fd` without `-X rm` first. Alternatively, use `rm`s "interactive"
option:
``` bash
> fd -H '^\.DS_Store$' -tf -X rm -i
```
If you also want to remove a certain class of directories, you can use the same technique. You will
have to use `rm`s `--recursive`/`-r` flag to remove directories.
Note: there are scenarios where using `fd … -X rm -r` can cause race conditions: if you have a
path like `…/foo/bar/foo/…` and want to remove all directories named `foo`, you can end up in a
situation where the outer `foo` directory is removed first, leading to (harmless) *"'foo/bar/foo':
No such file or directory"* errors in the `rm` call.
### Command-line options
This is the output of `fd -h`. To see the full set of command-line options, use `fd --help` which
also includes a much more detailed help text.
```
USAGE:
fd [FLAGS/OPTIONS] [<pattern>] [<path>...]
FLAGS:
-H, --hidden Search hidden files and directories
-I, --no-ignore Do not respect .(git|fd)ignore files
-s, --case-sensitive Case-sensitive search (default: smart case)
-i, --ignore-case Case-insensitive search (default: smart case)
-g, --glob Glob-based search (default: regular expression)
-a, --absolute-path Show absolute instead of relative paths
-l, --list-details Use a long listing format with file metadata
-L, --follow Follow symbolic links
-p, --full-path Search full path (default: file-/dirname only)
-0, --print0 Separate results by the null character
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-d, --max-depth <depth> Set maximum search depth (default: none)
-t, --type <filetype>... Filter by type: file (f), directory (d), symlink (l),
executable (x), empty (e), socket (s), pipe (p)
-e, --extension <ext>... Filter by file extension
-x, --exec <cmd> Execute a command for each search result
-X, --exec-batch <cmd> Execute a command with all search results at once
-E, --exclude <pattern>... Exclude entries that match the given glob pattern
-c, --color <when> When to use colors: never, *auto*, always
-S, --size <size>... Limit results based on the size of files.
--changed-within <date|dur> Filter by file modification time (newer than)
--changed-before <date|dur> Filter by file modification time (older than)
ARGS:
<pattern> the search pattern - a regular expression unless '--glob' is used (optional)
<path>... the root directory for the filesystem search (optional)
```
## Benchmark
Let's search my home folder for files that end in `[0-9].jpg`. It contains ~190.000
@@ -88,71 +358,153 @@ be different for you! I encourage everyone to try it out on their own. See
Concerning *fd*'s speed, the main credit goes to the `regex` and `ignore` crates that are also used
in [ripgrep](https://github.com/BurntSushi/ripgrep) (check it out!).
## Colorized output
## Troubleshooting
### Colorized output
`fd` can colorize files by extension, just like `ls`. In order for this to work, the environment
variable [`LS_COLORS`](https://linux.die.net/man/5/dir_colors) has to be set. Typically, the value
of this variable is set by the `dircolors` command which provides a convenient configuration format
to define colors for different file formats.
On most distributions, `LS_COLORS` should be set already. If you are looking for alternative, more
complete (and more colorful) variants, see
On most distributions, `LS_COLORS` should be set already. If you are on Windows or if you are looking
for alternative, more complete (or more colorful) variants, see [here](https://github.com/sharkdp/vivid),
[here](https://github.com/seebi/dircolors-solarized) or
[here](https://github.com/trapd00r/LS_COLORS).
`fd` also honors the [`NO_COLOR`](https://no-color.org/) environment variable.
## Parallel command execution
If the `-x`/`--exec` option is specified alongside a command template, a job pool will be created
for executing commands in parallel for each discovered path as the input. The syntax for generating
commands is similar to that of GNU Parallel:
- `{}`: A placeholder token that will be replaced with the path of the search result
(`documents/images/party.jpg`).
- `{.}`: Like `{}`, but without the file extension (`documents/images/party`).
- `{/}`: A placeholder that will be replaced by the basename of the search result (`party.jpg`).
- `{//}`: Uses the parent of the discovered path (`documents/images`).
- `{/.}`: Uses the basename, with the extension removed (`party`).
### `fd` does not find my file!
Remember that `fd` ignores hidden directories and files by default. It also ignores patterns
from `.gitignore` files. If you want to make sure to find absolutely every possible file, always
use the options `-H` and `-I` to disable these two features:
``` bash
# Convert all jpg files to png files:
fd -e jpg -x convert {} {.}.png
# Unpack all zip files (if no placeholder is given, the path is appended):
fd -e zip -x unzip
# Convert all flac files into opus files:
fd -e flac -x ffmpeg -i {} -c:a libopus {.}.opus
# Count the number of lines in Rust files (the command template can be terminated with ';'):
fd -x wc -l \; -e rs
> fd -HI …
```
The number of threads used for command execution can be set with the `--threads`/`-j` option.
### `fd` doesn't seem to interpret my regex pattern correctly
A lot of special regex characters (like `[]`, `^`, `$`, ..) are also special characters in your
shell. If in doubt, always make sure to put single quotes around the regex pattern:
``` bash
> fd '^[A-Z][0-9]+$'
```
If your pattern starts with a dash, you have to add `--` to signal the end of command line
options. Otherwise, the pattern will be interpreted as a command-line option. Alternatively,
use a character class with a single hyphen character:
``` bash
> fd -- '-pattern'
> fd '[-]pattern'
```
## Integration with other programs
### Using fd with `fzf`
You can use *fd* to generate input for the command-line fuzzy finder [fzf](https://github.com/junegunn/fzf):
``` bash
export FZF_DEFAULT_COMMAND='fd --type file'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
```
Then, you can type `vim <Ctrl-T>` on your terminal to open fzf and search through the fd-results.
Alternatively, you might like to follow symbolic links and include hidden files (but exclude `.git` folders):
``` bash
export FZF_DEFAULT_COMMAND='fd --type file --follow --hidden --exclude .git'
```
You can even use fd's colored output inside fzf by setting:
``` bash
export FZF_DEFAULT_COMMAND="fd --type file --color=always"
export FZF_DEFAULT_OPTS="--ansi"
```
For more details, see the [Tips section](https://github.com/junegunn/fzf#tips) of the fzf README.
### Using fd with `rofi`
[*rofi*](https://github.com/davatorium/rofi) is a graphical launch menu application that is able to create menus by reading from *stdin*. Piping `fd` output into `rofi`s `-dmenu` mode creates fuzzy-searchable lists of files and directories.
#### Example
Create a case-insensitive searchable multi-select list of *PDF* files under your `$HOME` directory and open the selection with your configured PDF viewer. To list all file types, drop the `-e pdf` argument.
``` bash
fd --type f -e pdf . $HOME | rofi -keep-right -dmenu -i -p FILES -multi-select | xargs -I {} xdg-open {}
```
To modify the list that is presented by rofi, add arguments to the `fd` command. To modify the search behaviour of rofi, add arguments to the `rofi` command.
### Using fd with `emacs`
The emacs package [find-file-in-project](https://github.com/technomancy/find-file-in-project) can
use *fd* to find files.
After installing `find-file-in-project`, add the line `(setq ffip-use-rust-fd t)` to your
`~/.emacs` or `~/.emacs.d/init.el` file.
In emacs, run `M-x find-file-in-project-by-selected` to find matching files. Alternatively, run
`M-x find-file-in-project` to list all available files in the project.
### Printing the output as a tree
To format the output of `fd` similar to the `tree` command, install [`as-tree`] and pipe the output
of `fd` to `as-tree`:
```bash
fd | as-tree
```
This can be more useful than running `tree` by itself because `tree` does not ignore any files by
default, nor does it support as rich a set of options as `fd` does to control what to print:
```bash
fd --extension rs | as-tree
.
├── build.rs
└── src
├── app.rs
└── error.rs
```
For more information about `as-tree`, see [the `as-tree` README][`as-tree`].
[`as-tree`]: https://github.com/jez/as-tree
### Using fd with `xargs` or `parallel`
Note that `fd` has a builtin feature for [command execution](#command-execution) with
its `-x`/`--exec` and `-X`/`--exec-batch` options. If you prefer, you can still use
it in combination with `xargs`:
``` bash
> fd -0 -e rs | xargs -0 wc -l
```
Here, the `-0` option tells *fd* to separate search results by the NULL character (instead of
newlines). In the same way, the `-0` option of `xargs` tells it to read the input in this way.
## Installation
[![Packaging status](https://repology.org/badge/vertical-allrepos/fd.svg)](https://repology.org/project/fd/versions)
[![Packaging status](https://repology.org/badge/vertical-allrepos/rust:fd-find.svg)](https://repology.org/project/rust:fd-find/versions)
[![Packaging status](https://repology.org/badge/vertical-allrepos/fd-find.svg)](https://repology.org/project/fd-find/versions)
### On Ubuntu
*... and other Debian-based Linux distributions.*
If you run Ubuntu 19.04 (Disco Dingo) or newer, you can install the
[officially maintained package](https://packages.ubuntu.com/disco/fd-find):
[officially maintained package](https://packages.ubuntu.com/fd-find):
```
sudo apt install fd-find
```
Note that the binary is called `fdfind` as the binary name `fd` is already used by another package.
It is recommended that you add an `alias fd=fdfind` to your shells initialization file, in order to
use `fd` in the same way as in this documentation.
It is recommended that after installation, you add a link to `fd` by executing command
`ln -s $(which fdfind) ~/.local/bin/fd`, in order to use `fd` in the same way as in this documentation.
Make sure that `$HOME/.local/bin` is in your `$PATH`.
If you use an older version of Ubuntu, you can download the latest `.deb` package from the
[release page](https://github.com/sharkdp/fd/releases) and install it via:
``` bash
sudo dpkg -i fd_8.1.0_amd64.deb # adapt version number and architecture
sudo dpkg -i fd_8.2.1_amd64.deb # adapt version number and architecture
```
### On Debian
@@ -163,8 +515,9 @@ If you run Debian Buster or newer, you can install the
sudo apt-get install fd-find
```
Note that the binary is called `fdfind` as the binary name `fd` is already used by another package.
It is recommended that you add an `alias fd=fdfind` to your shells initialization file, in order to
use `fd` in the same way as in this documentation.
It is recommended that after installation, you add a link to `fd` by executing command
`ln -s $(which fdfind) ~/.local/bin/fd`, in order to use `fd` in the same way as in this documentation.
Make sure that `$HOME/.local/bin` is in your `$PATH`.
### On Fedora
@@ -268,11 +621,11 @@ With Rust's package manager [cargo](https://github.com/rust-lang/cargo), you can
```
cargo install fd-find
```
Note that rust version *1.36.0* or later is required.
Note that rust version *1.42.0* or later is required.
### From binaries
The [release page](https://github.com/sharkdp/fd/releases) includes precompiled binaries for Linux, macOS and Windows.
The [release page](https://github.com/sharkdp/fd/releases) includes precompiled binaries for Linux, macOS and Windows. Statically-linked binaries are also available: look for archives with `musl` in the file name.
## Development
```bash
@@ -286,303 +639,18 @@ cargo build
cargo test
# Install
cargo install
cargo install --path .
```
## Command-line options
```
USAGE:
fd [FLAGS/OPTIONS] [<pattern>] [<path>...]
## Maintainers
FLAGS:
-H, --hidden Search hidden files and directories
-I, --no-ignore Do not respect .(git|fd)ignore files
-s, --case-sensitive Case-sensitive search (default: smart case)
-i, --ignore-case Case-insensitive search (default: smart case)
-g, --glob Glob-based search (default: regular expression)
-a, --absolute-path Show absolute instead of relative paths
-l, --list-details Use a long listing format with file metadata
-L, --follow Follow symbolic links
-p, --full-path Search full path (default: file-/dirname only)
-0, --print0 Separate results by the null character
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-d, --max-depth <depth> Set maximum search depth (default: none)
-t, --type <filetype>... Filter by type: file (f), directory (d), symlink (l),
executable (x), empty (e), socket (s), pipe (p)
-e, --extension <ext>... Filter by file extension
-x, --exec <cmd> Execute a command for each search result
-X, --exec-batch <cmd> Execute a command with all search results at once
-E, --exclude <pattern>... Exclude entries that match the given glob pattern
-c, --color <when> When to use colors: never, *auto*, always
-S, --size <size>... Limit results based on the size of files.
--changed-within <date|dur> Filter by file modification time (newer than)
--changed-before <date|dur> Filter by file modification time (older than)
ARGS:
<pattern> the search pattern - a regular expression unless '--glob' is used (optional)
<path>... the root directory for the filesystem search (optional)
```
This is the output of `fd -h`. To see the full set of command-line options, use `fd --help` which
also includes a much more detailed help text.
## Tutorial
First, to get an overview of all available command line options, you can either run
`fd -h` for a concise help message (see above) or `fd --help` for a more detailed
version.
### Simple search
*fd* is designed to find entries in your filesystem. The most basic search you can perform is to
run *fd* with a single argument: the search pattern. For example, assume that you want to find an
old script of yours (the name included `netflix`):
``` bash
> fd netfl
Software/python/imdb-ratings/netflix-details.py
```
If called with just a single argument like this, *fd* searches the current directory recursively
for any entries that *contain* the pattern `netfl`.
### Regular expression search
The search pattern is treated as a regular expression. Here, we search for entries that start
with `x` and end with `rc`:
``` bash
> cd /etc
> fd '^x.*rc$'
X11/xinit/xinitrc
X11/xinit/xserverrc
```
### Specifying the root directory
If we want to search a specific directory, it can be given as a second argument to *fd*:
``` bash
> fd passwd /etc
/etc/default/passwd
/etc/pam.d/passwd
/etc/passwd
```
### Running *fd* without any arguments
*fd* can be called with no arguments. This is very useful to get a quick overview of all entries
in the current directory, recursively (similar to `ls -R`):
``` bash
> cd fd/tests
> fd
testenv
testenv/mod.rs
tests.rs
```
If you want to use this functionality to list all files in a given directory, you have to use
a catch-all pattern such as `.` or `^`:
``` bash
> fd . fd/tests/
testenv
testenv/mod.rs
tests.rs
```
### Searching for a particular file extension
Often, we are interested in all files of a particular type. This can be done with the `-e` (or
`--extension`) option. Here, we search for all Markdown files in the fd repository:
``` bash
> cd fd
> fd -e md
CONTRIBUTING.md
README.md
```
The `-e` option can be used in combination with a search pattern:
``` bash
> fd -e rs mod
src/fshelper/mod.rs
src/lscolors/mod.rs
tests/testenv/mod.rs
```
### Hidden and ignored files
By default, *fd* does not search hidden directories and does not show hidden files in the
search results. To disable this behavior, we can use the `-H` (or `--hidden`) option:
``` bash
> fd pre-commit
> fd -H pre-commit
.git/hooks/pre-commit.sample
```
If we work in a directory that is a Git repository (or includes Git repositories), *fd* does not
search folders (and does not show files) that match one of the `.gitignore` patterns. To disable
this behavior, we can use the `-I` (or `--no-ignore`) option:
``` bash
> fd num_cpu
> fd -I num_cpu
target/debug/deps/libnum_cpus-f5ce7ef99006aa05.rlib
```
To really search *all* files and directories, simply combine the hidden and ignore features to show
everything (`-HI`).
### Excluding specific files or directories
Sometimes we want to ignore search results from a specific subdirectory. For example, we might
want to search all hidden files and directories (`-H`) but exclude all matches from `.git`
directories. We can use the `-E` (or `--exclude`) option for this. It takes an arbitrary glob
pattern as an argument:
``` bash
> fd -H -E .git …
```
We can also use this to skip mounted directories:
``` bash
> fd -E /mnt/external-drive …
```
.. or to skip certain file types:
``` bash
> fd -E '*.bak' …
```
To make exclude-patterns like these permanent, you can create a `.fdignore` file. They work like
`.gitignore` files, but are specific to `fd`. For example:
``` bash
> cat ~/.fdignore
/mnt/external-drive
*.bak
```
Note: `fd` also supports `.ignore` files that are used by other programs such as `rg` or `ag`.
If you want `fd` to ignore these patterns globally, you can put them in `fd`'s global ignore file.
This is usually located in `~/.config/fd/ignore` in macOS or Linux, and `%APPDATA%\fd\ignore` in
Windows.
### Using fd with `xargs` or `parallel`
If we want to run a command on all search results, we can pipe the output to `xargs`:
``` bash
> fd -0 -e rs | xargs -0 wc -l
```
Here, the `-0` option tells *fd* to separate search results by the NULL character (instead of
newlines). In the same way, the `-0` option of `xargs` tells it to read the input in this way.
### Deleting files
You can use `fd` to remove all files and directories that are matched by your search pattern.
If you only want to remove files, you can use the `--exec-batch`/`-X` option to call `rm`. For
example, to recursively remove all `.DS_Store` files, run:
``` bash
> fd -H '^\.DS_Store$' -tf -X rm
```
If you are unsure, always call `fd` without `-X rm` first. Alternatively, use `rm`s "interactive"
option:
``` bash
> fd -H '^\.DS_Store$' -tf -X rm -i
```
If you also want to remove a certain class of directories, you can use the same technique. You will
have to use `rm`s `--recursive`/`-r` flag to remove directories.
Note: there are scenarios where using `fd … -X rm -r` can cause race conditions: if you have a
path like `…/foo/bar/foo/…` and want to remove all directories named `foo`, you can end up in a
situation where the outer `foo` directory is removed first, leading to (harmless) *"'foo/bar/foo':
No such file or directory"* errors in the `rm` call.
### Troubleshooting
#### `fd` does not find my file!
Remember that `fd` ignores hidden directories and files by default. It also ignores patterns
from `.gitignore` files. If you want to make sure to find absolutely every possible file, always
use the options `-H` and `-I` to disable these two features:
``` bash
> fd -HI …
```
#### `fd` doesn't seem to interpret my regex pattern correctly
A lot of special regex characters (like `[]`, `^`, `$`, ..) are also special characters in your
shell. If in doubt, always make sure to put single quotes around the regex pattern:
``` bash
> fd '^[A-Z][0-9]+$'
```
If your pattern starts with a dash, you have to add `--` to signal the end of command line
options. Otherwise, the pattern will be interpreted as a command-line option. Alternatively,
use a character class with a single hyphen character:
``` bash
> fd -- '-pattern'
> fd '[-]pattern'
```
### Integration with other programs
#### Using fd with `fzf`
You can use *fd* to generate input for the command-line fuzzy finder [fzf](https://github.com/junegunn/fzf):
``` bash
export FZF_DEFAULT_COMMAND='fd --type file'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
```
Then, you can type `vim <Ctrl-T>` on your terminal to open fzf and search through the fd-results.
Alternatively, you might like to follow symbolic links and include hidden files (but exclude `.git` folders):
``` bash
export FZF_DEFAULT_COMMAND='fd --type file --follow --hidden --exclude .git'
```
You can even use fd's colored output inside fzf by setting:
``` bash
export FZF_DEFAULT_COMMAND="fd --type file --color=always"
export FZF_DEFAULT_OPTS="--ansi"
```
For more details, see the [Tips section](https://github.com/junegunn/fzf#tips) of the fzf README.
#### Using fd with `emacs`
The emacs package [find-file-in-project](https://github.com/technomancy/find-file-in-project) can
use *fd* to find files.
After installing `find-file-in-project`, add the line `(setq ffip-use-rust-fd t)` to your
`~/.emacs` or `~/.emacs.d/init.el` file.
In emacs, run `M-x find-file-in-project-by-selected` to find matching files. Alternatively, run
`M-x find-file-in-project` to list all available files in the project.
#### Printing fd's output as a tree
To format the output of `fd` similar to the `tree` command, install [`as-tree`] and pipe the output
of `fd` to `as-tree`:
```bash
fd | as-tree
```
This can be more useful than running `tree` by itself because `tree` does not ignore any files by
default, nor does it support as rich a set of options as `fd` does to control what to print:
```bash
fd --extension rs | as-tree
.
├── build.rs
└── src
├── app.rs
└── error.rs
```
For more information about `as-tree`, see [the `as-tree` README][`as-tree`].
[`as-tree`]: https://github.com/jez/as-tree
- [sharkdp](https://github.com/sharkdp)
- [tmccombs](https://github.com/tmccombs)
- [tavianator](https://github.com/tavianator)
## License
Copyright (c) 2017-2020 The fd developers
Copyright (c) 2017-2021 The fd developers
`fd` is distributed under the terms of both the MIT License and the Apache License 2.0.
-71
View File
@@ -1,71 +0,0 @@
# Source: https://github.com/starkat99/appveyor-rust/
environment:
global:
PROJECT_NAME: fd
matrix:
# Stable channel
- TARGET: i686-pc-windows-gnu
CHANNEL: stable
- TARGET: i686-pc-windows-msvc
CHANNEL: stable
- TARGET: x86_64-pc-windows-gnu
CHANNEL: stable
- TARGET: x86_64-pc-windows-msvc
CHANNEL: stable
# Install Rust and Cargo
# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml)
install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs
- rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv
- cargo -V
# 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents
# the "directory does not contain a project or solution file" error.
# source: https://github.com/starkat99/appveyor-rust/blob/master/appveyor.yml#L113
build: false
# Equivalent to Travis' `script` phase
test_script:
- cargo build --verbose
- cargo run
- cargo test
before_deploy:
# Generate artifacts for release
- cargo build --release
- mkdir staging
- copy target\release\fd.exe staging
- ps: copy target\release\build\fd-find*\out\_fd.ps1 staging
- cd staging
# release zipfile will look like 'rust-everywhere-v1.2.3-x86_64-pc-windows-msvc'
- 7z a ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip *
- appveyor PushArtifact ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip
deploy:
description: 'Windows release'
# All the zipped artifacts will be deployed
artifact: /.*\.zip/
# Here's how:
# - Go to 'https://github.com/settings/tokens/new' and generate a Token with only the
# `public_repo` scope enabled
# - Then go to 'https://ci.appveyor.com/tools/encrypt' and enter the newly generated token.
# - Enter the "encrypted value" below
auth_token:
secure: mWJ8ieZdGEgHf232fdMyzb9T1rKbkJivYbp/REMo8ax3X5vkQJDHhFjWeuWA3wIC
provider: GitHub
# deploy when a new tag is pushed and only on the stable channel
on:
# channel to use to produce the release artifacts
CHANNEL: stable
appveyor_repo_tag: true
branches:
only:
- master
# IMPORTANT Regex to match tags. Required, or appveyor may not trigger deploys when a new tag
# is pushed. This regex matches semantic versions like v1.2.3-rc4+2016.02.22
- /^v\d+\.\d+\.\d+.*$/
+2 -3
View File
@@ -5,7 +5,7 @@ use clap::Shell;
include!("src/app.rs");
fn main() {
let min_version = "1.36";
let min_version = "1.42";
match version_check::is_min_version(min_version) {
Some(true) => {}
@@ -16,7 +16,7 @@ fn main() {
}
}
let var = std::env::var_os("SHELL_COMPLETIONS_DIR").or(std::env::var_os("OUT_DIR"));
let var = std::env::var_os("SHELL_COMPLETIONS_DIR").or_else(|| std::env::var_os("OUT_DIR"));
let outdir = match var {
None => return,
Some(outdir) => outdir,
@@ -26,6 +26,5 @@ fn main() {
let mut app = build_app();
app.gen_completions("fd", Shell::Bash, &outdir);
app.gen_completions("fd", Shell::Fish, &outdir);
app.gen_completions("fd", Shell::Zsh, &outdir);
app.gen_completions("fd", Shell::PowerShell, &outdir);
}
-1
View File
@@ -1 +0,0 @@
* linguist-vendored
-179
View File
@@ -1,179 +0,0 @@
#!/usr/bin/env bash
# Building and packaging for release
set -ex
build() {
cargo build --target "$TARGET" --release --verbose
}
pack() {
local tempdir
local out_dir
local package_name
local gcc_prefix
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
out_dir=$(pwd)
package_name="$PROJECT_NAME-$TRAVIS_TAG-$TARGET"
if [[ $TARGET == arm-unknown-linux-* ]]; then
gcc_prefix="arm-linux-gnueabihf-"
else
gcc_prefix=""
fi
# create a "staging" directory
mkdir "$tempdir/$package_name"
mkdir "$tempdir/$package_name/autocomplete"
# copying the main binary
cp "target/$TARGET/release/$PROJECT_NAME" "$tempdir/$package_name/"
"${gcc_prefix}"strip "$tempdir/$package_name/$PROJECT_NAME"
# manpage, readme and license
cp "doc/$PROJECT_NAME.1" "$tempdir/$package_name"
cp README.md "$tempdir/$package_name"
cp LICENSE-MIT "$tempdir/$package_name"
cp LICENSE-APACHE "$tempdir/$package_name"
# various autocomplete
cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/"$PROJECT_NAME".bash "$tempdir/$package_name/autocomplete/${PROJECT_NAME}.bash-completion"
cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/"$PROJECT_NAME".fish "$tempdir/$package_name/autocomplete"
cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/_"$PROJECT_NAME" "$tempdir/$package_name/autocomplete"
# archiving
pushd "$tempdir"
tar czf "$out_dir/$package_name.tar.gz" "$package_name"/*
popd
rm -r "$tempdir"
}
make_deb() {
local tempdir
local architecture
local version
local dpkgname
local conflictname
local homepage
local maintainer
local gcc_prefix
homepage="https://github.com/sharkdp/fd"
maintainer="David Peter <mail@david-peter.de>"
case $TARGET in
x86_64*)
architecture=amd64
gcc_prefix=""
;;
i686*)
architecture=i386
gcc_prefix=""
;;
arm*hf)
architecture=armhf
gcc_prefix="arm-linux-gnueabihf-"
;;
*)
echo "make_deb: skipping target '${TARGET}'" >&2
return 0
;;
esac
version=${TRAVIS_TAG#v}
if [[ $TARGET = *musl* ]]; then
dpkgname=$PROJECT_NAME-musl
conflictname=$PROJECT_NAME
else
dpkgname=$PROJECT_NAME
conflictname=$PROJECT_NAME-musl
fi
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
# copy the main binary
install -Dm755 "target/$TARGET/release/$PROJECT_NAME" "$tempdir/usr/bin/$PROJECT_NAME"
"${gcc_prefix}"strip "$tempdir/usr/bin/$PROJECT_NAME"
# manpage
install -Dm644 "doc/$PROJECT_NAME.1" "$tempdir/usr/share/man/man1/$PROJECT_NAME.1"
gzip --best "$tempdir/usr/share/man/man1/$PROJECT_NAME.1"
# readme and license
install -Dm644 README.md "$tempdir/usr/share/doc/$PROJECT_NAME/README.md"
install -Dm644 LICENSE-MIT "$tempdir/usr/share/doc/$PROJECT_NAME/LICENSE-MIT"
install -Dm644 LICENSE-APACHE "$tempdir/usr/share/doc/$PROJECT_NAME/LICENSE-APACHE"
cat > "$tempdir/usr/share/doc/$PROJECT_NAME/copyright" <<EOF
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: $PROJECT_NAME
Source: $homepage
Files: *
Copyright: $maintainer
License: Apache-2.0 or MIT
License: Apache-2.0
On Debian systems, the complete text of the Apache-2.0 can be found in the
file /usr/share/common-licenses/Apache-2.0.
License: MIT
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
.
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
EOF
# completions
install -Dm644 target/$TARGET/release/build/$PROJECT_NAME-*/out/$PROJECT_NAME.bash "$tempdir/usr/share/bash-completion/completions/${PROJECT_NAME}"
install -Dm644 target/$TARGET/release/build/$PROJECT_NAME-*/out/$PROJECT_NAME.fish "$tempdir/usr/share/fish/completions/$PROJECT_NAME.fish"
install -Dm644 target/$TARGET/release/build/$PROJECT_NAME-*/out/_$PROJECT_NAME "$tempdir/usr/share/zsh/vendor-completions/_$PROJECT_NAME"
# Control file
mkdir "$tempdir/DEBIAN"
cat > "$tempdir/DEBIAN/control" <<EOF
Package: $dpkgname
Version: $version
Section: utils
Priority: optional
Maintainer: $maintainer
Architecture: $architecture
Provides: $PROJECT_NAME
Conflicts: $conflictname
Homepage: $homepage
Description: Simple, fast and user-friendly alternative to find
While fd does not seek to mirror all of find's powerful functionality, it
provides sensible (opinionated) defaults for 80% of the use cases.
EOF
fakeroot dpkg-deb --build "$tempdir" "${dpkgname}_${version}_${architecture}.deb"
}
main() {
build
pack
if [[ $TARGET = *linux* ]]; then
make_deb
fi
}
main
-26
View File
@@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -ex
if [ "$TRAVIS_OS_NAME" != linux ]; then
exit 0
fi
sudo apt-get update
# needed to build deb packages
sudo apt-get install -y fakeroot
# needed for i686 linux gnu target
if [[ $TARGET == i686-unknown-linux-gnu ]]; then
sudo apt-get install -y gcc-multilib
fi
# needed for cross-compiling for arm
if [[ $TARGET == arm-unknown-linux-* ]]; then
sudo apt-get install -y \
gcc-4.8-arm-linux-gnueabihf \
binutils-arm-linux-gnueabihf \
libc6-armhf-cross \
libc6-dev-armhf-cross
fi
-11
View File
@@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -ex
# Incorporate TARGET env var to the build and test process
cargo build --target "$TARGET" --verbose
# We cannot run arm executables on linux
if [[ $TARGET != arm-unknown-linux-* ]]; then
cargo test --target "$TARGET" --verbose
fi
+268
View File
@@ -0,0 +1,268 @@
#compdef fd
##
# zsh completion function for fd
#
# Based on ripgrep completion function.
# Originally based on code from the zsh-users project — see copyright notice
# below.
autoload -U is-at-least
_fd() {
local curcontext="$curcontext" no='!' ret=1
local -a context line state state_descr _arguments_options fd_types fd_args
local -A opt_args
if is-at-least 5.2; then
_arguments_options=( -s -S )
else
_arguments_options=( -s )
fi
fd_types=(
{f,file}'\:"regular files"'
{d,directory}'\:"directories"'
{l,symlink}'\:"symbolic links"'
{e,empty}'\:"empty files or directories"'
{x,executable}'\:"executable (files)"'
{s,socket}'\:"sockets"'
{p,pipe}'\:"named pipes (FIFOs)"'
)
# Do not complete rare options unless either the current prefix
# matches one of those options or the user has the `complete-all`
# style set. Note that this prefix check has to be updated manually to account
# for all of the potential negation options listed below!
if
# (--[bpsu]* => match all options marked with '$no')
[[ $PREFIX$SUFFIX == --[bopsu]* ]] ||
zstyle -t ":complete:$curcontext:*" complete-all
then
no=
fi
# We make heavy use of argument groups here to prevent the option specs from
# growing unwieldy. These aren't supported in zsh <5.4, though, so we'll strip
# them out below if necessary. This makes the exclusions inaccurate on those
# older versions, but oh well — it's not that big a deal
fd_args=(
+ '(hidden)' # hidden files
{-H,--hidden}'[search hidden files/directories]'
+ '(no-ignore-full)' # all ignore files
'(no-ignore-partial)'{-I,--no-ignore}"[don't respect .(git|fd)ignore and global ignore files]"
$no'(no-ignore-partial)*'{-u,--unrestricted}'[alias for --no-ignore, when repeated also alias for --hidden]'
+ no-ignore-partial # some ignore files
"(no-ignore-full --no-ignore-vcs)--no-ignore-vcs[don't respect .gitignore files]"
"!(no-ignore-full --no-global-ignore-file)--no-global-ignore-file[don't respect the global ignore file]"
+ '(case)' # case-sensitivity
{-s,--case-sensitive}'[perform a case-sensitive search]'
{-i,--ignore-case}'[perform a case-insensitive search]'
+ '(regex-pattern)' # regex-based search pattern
'(no-regex-pattern)--regex[perform a regex-based search (default)]'
+ '(no-regex-pattern)' # non-regex-based search pattern
{-g,--glob}'[perform a glob-based search]'
{-F,--fixed-strings}'[treat pattern as literal string instead of a regex]'
+ '(match-full)' # match against full path
{-p,--full-path}'[match the pattern against the full path instead of the basename]'
+ '(follow)' # follow symlinks
{-L,--follow}'[follow symbolic links to directories]'
+ '(abs-path)' # show absolute paths
'(long-listing)'{-a,--absolute-path}'[show absolute paths instead of relative paths]'
+ '(null-sep)' # use null separator for output
'(long-listing)'{-0,--print0}'[separate search results by the null character]'
+ '(long-listing)' # long-listing output
'(abs-path null-sep max-results exec-cmds)'{-l,--list-details}'[use a long listing format with file metadata]'
+ '(max-results)' # max number of results
'(long-listing exec-cmds)--max-results=[limit number of search results to given count and quit]:count'
'(long-listing exec-cmds)-1[limit to a single search result and quit]'
+ '(fs-errors)' # file-system errors
$no'--show-errors[enable the display of filesystem errors]'
+ '(fs-traversal)' # file-system traversal
$no"--one-file-system[don't descend into directories on other file systems]"
'!--mount'
'!--xdev'
+ dir-depth # directory depth
'(--exact-depth -d --max-depth)'{-d+,--max-depth=}'[set max directory depth to descend when searching]:depth'
'!(--exact-depth -d --max-depth)--maxdepth:depth'
'(--exact-depth --min-depth)--min-depth=[set directory depth to descend before start searching]:depth'
'(--exact-depth -d --max-depth --maxdepth --min-depth)--exact-depth=[only search at the exact given directory depth]:depth'
+ prune # pruning
"--prune[don't traverse into matching directories]"
+ filter-misc # filter search
'*'{-t+,--type=}"[filter search by type]:type:(($fd_types))"
'*'{-e+,--extension=}'[filter search by file extension]:extension'
'*'{-E+,--exclude=}'[exclude files/directories that match the given glob pattern]:glob pattern'
'*'{-S+,--size=}'[limit search by file size]:size limit:->size'
'(-o --owner)'{-o+,--owner=}'[filter by owning user and/or group]:owner and/or group:->owner'
+ ignore-file # extra ignore files
'*--ignore-file=[add a custom, low-precedence ignore-file with .gitignore format]: :_files'
+ '(filter-mtime-newer)' # filter by files modified after than
'--changed-within=[limit search to files/directories modified within the given date/duration]:date or duration'
'!--change-newer-than=:date/duration'
'!--newer=:date/duration'
+ '(filter-mtime-older)' # filter by files modified before than
'--changed-before=[limit search to files/directories modified before the given date/duration]:date or duration'
'!--change-older-than=:date/duration'
'!--older=:date/duration'
+ '(color)' # colorize output
{-c+,--color=}'[declare when to colorize search results]:when to colorize:((
auto\:"show colors if the output goes to an interactive console (default)"
never\:"do not use colorized output"
always\:"always use colorized output"
))'
+ '(threads)'
{-j+,--threads=}'[set the number of threads for searching and executing]:number of threads'
+ '(exec-cmds)' # execute command
'(long-listing max-results)'{-x+,--exec=}'[execute command for each search result]:command: _command_names -e:*\;::program arguments: _normal'
'(long-listing max-results)'{-X+,--exec-batch=}'[execute command for all search results at once]:command: _command_names -e:*\;::program arguments: _normal'
+ other
'!(--max-buffer-time)--max-buffer-time=[set amount of time to buffer before showing output]:time (ms)'
+ '(about)' # about flags
'(: * -)'{-h,--help}'[display help message]'
'(: * -)'{-v,--version}'[display version information]'
+ path-sep # set path separator for output
$no'(--path-separator)--path-separator=[set the path separator to use when printing file paths]:path separator'
+ search-path
$no'(--base-directory)--base-directory=[change the current working directory to the given path]:directory:_files -/'
$no'(*)*--search-path=[set search path (instead of positional <path> arguments)]:directory:_files -/'
+ args # positional arguments
'1: :_guard "^-*" pattern'
'(--search-path)*:directory:_files -/'
)
# Strip out argument groups where unsupported (see above)
is-at-least 5.4 ||
fd_args=( ${(@)args:#(#i)(+|[a-z0-9][a-z0-9_-]#|\([a-z0-9][a-z0-9_-]#\))} )
_arguments $_arguments_options : $fd_args && ret=0
case ${state} in
owner)
compset -P '(\\|)\!'
if compset -P '*:'; then
_groups && ret=0
else
if
compset -S ':*' ||
# Do not add the colon suffix when completing "!user<TAB>
# (with a starting double-quote) otherwise pressing tab again
# after the inserted colon "!user:<TAB> will complete history modifiers
[[ $IPREFIX == (\\|\!)* && ($QIPREFIX == \"* && -z $QISUFFIX) ]]
then
_users && ret=0
else
local q
# Since quotes are needed when using the negation prefix !,
# automatically remove the colon suffix also when closing the quote
if [[ $QIPREFIX == [\'\"]* ]]; then
q=${QIPREFIX:0:1}
fi
_users -r ": \t\n\-$q" -S : && ret=0
fi
fi
;;
size)
if compset -P '[-+][0-9]##'; then
local -a suff=(
'B:bytes'
'K:kilobytes (10^3 = 1000 bytes)'
'M:megabytes (10^6 = 1000^2 bytes)'
'G:gigabytes (10^9 = 1000^3 bytes)'
'T:terabytes (10^12 = 1000^4 bytes)'
'Ki:kibibytes ( 2^10 = 1024 bytes)'
'Mi:mebibytes ( 2^20 = 1024^2 bytes)'
'Gi:gigibytes ( 2^30 = 1024^3 bytes)'
'Ti:tebibytes ( 2^40 = 1024^4 bytes)'
)
_describe -t units 'size limit units' suff -V 'units'
elif compset -P '[-+]'; then
_message -e 'size limit number (full format: <+-><number><unit>)'
else
_values 'size limit prefix (full format: <prefix><number><unit>)' \
'\+[file size must be greater or equal to]'\
'-[file size must be less than or equal to]' && ret=0
fi
;;
esac
return ret
}
_fd "$@"
# ------------------------------------------------------------------------------
# Copyright (c) 2011 Github zsh-users - http://github.com/zsh-users
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the zsh-users nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL ZSH-USERS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ------------------------------------------------------------------------------
# Description
# -----------
#
# Completion script for fd
#
# ------------------------------------------------------------------------------
# Authors
# -------
#
# * smancill (https://github.com/smancill)
#
# ------------------------------------------------------------------------------
# Local Variables:
# mode: shell-script
# coding: utf-8-unix
# indent-tabs-mode: nil
# sh-indentation: 2
# sh-basic-offset: 2
# End:
# vim: ft=zsh sw=2 ts=2 et
Vendored
+83 -12
View File
@@ -57,10 +57,11 @@ pattern contains an uppercase character (smart case).
Perform a glob-based search instead of a regular expression search.
.TP
.B \-\-regex
Perform a regular-expression based seach (default). This can be used to override --glob.
Perform a regular-expression based search (default). This can be used to override --glob.
.TP
.B \-F, \-\-fixed\-strings
Treat the pattern as a literal string instead of a regular expression.
Treat the pattern as a literal string instead of a regular expression. Note that this also
performs substring comparison. If you want to match on an exact filename, consider using '\-\-glob'.
.TP
.B \-a, \-\-absolute\-path
Shows the full path starting from the root as opposed to relative paths.
@@ -115,6 +116,9 @@ Only show search results starting at the given depth. See also: '--max-depth' an
.BI "\-\-exact\-depth " d
Only show search results at the exact given depth. This is an alias for '--min-depth <depth> --max-depth <depth>'.
.TP
.B \-\-prune
Do not traverse into matching directories.
.TP
.BI "\-t, \-\-type " filetype
Filter search by type:
.RS
@@ -142,11 +146,17 @@ This option can be used repeatedly to allow for multiple file types.
Filter search results by file extension
.IR ext .
This option can be used repeatedly to allow for multiple possible file extensions.
If you want to search for files without extension, you can use the regex '^[^.]+$'
as a normal search pattern.
.TP
.BI "\-E, \-\-exclude " pattern
Exclude files/directories that match the given glob pattern.
This overrides any other ignore logic.
Multiple exclude patterns can be specified.
Examples:
\-\-exclude '*.pyc'
\-\-exclude node_modules
.TP
.BI "\-\-ignore-file " path
Add a custom ignore-file in '.gitignore' format.
@@ -176,6 +186,8 @@ Limit results based on the size of files using the format
file size must be greater than or equal to this
.IP '-'
file size must be less than or equal to this
.P
If neither '+' nor '-' is specified, file size must be exactly equal to this.
.IP 'NUM'
The numeric size (e.g. 500)
.IP 'UNIT'
@@ -232,13 +244,37 @@ Examples:
\-\-owner :students
\-\-owner "!john:students"
.TP
.BI "\-\-base\-directory " path
Change the current working directory of fd to the provided path. This means that search results will
be shown with respect to the given base path. Note that relative paths which are passed to fd via the
positional \fIpath\fR argument or the \fB\-\-search\-path\fR option will also be resolved relative to
this directory.
.TP
.BI "\-\-path\-separator " separator
Set the path separator to use when printing file paths. The default is the OS-specific separator
('/' on Unix, '\\' on Windows).
.TP
.BI "\-\-search\-path " search\-path
Provide paths to search as an alternative to the positional \fIpath\fR argument. Changes the usage to
\'fd [FLAGS/OPTIONS] \-\-search\-path PATH \-\-search\-path PATH2 [PATTERN]\'
.TP
.BI "\-x, \-\-exec " command
.RS
Execute
.I command
for each search result. The following placeholders are substituted by a path derived from the current search result:
for each search result in parallel (use --threads=1 for sequential command execution).
Note that all subsequent positional arguments are considered to be arguments to the
.I command
- not to fd.
It is therefore recommended to place the \-x/\-\-exec option last. Alternatively, you can supply
a ';' argument to end the argument list and continue with more fd options.
Most shells require ';' to be escaped: '\\;'.
The following placeholders are substituted before the command is executed:
.RS
.IP {}
path
path (of the current search result)
.IP {/}
basename
.IP {//}
@@ -248,15 +284,33 @@ path without file extension
.IP {/.}
basename without file extension
.RE
If no placeholder is present, an implicit "{}" at the end is assumed.
Examples:
- find all *.zip files and unzip them:
fd -e zip -x unzip
- find *.h and *.cpp files and run "clang-format -i .." for each of them:
fd -e h -e cpp -x clang-format -i
- Convert all *.jpg files to *.png files:
fd -e jpg -x convert {} {.}.png
.RE
.TP
.BI "\-X, \-\-exec-batch " command
.RS
Execute
.I command
with all search results at once.
A single occurence of the following placeholders is authorized and substituted by the paths derived from the search results before the command is executed:
once, with all search results as arguments.
One of the following placeholders is substituted before the command is executed:
.RS
.IP {}
path
path (of all search results)
.IP {/}
basename
.IP {//}
@@ -266,22 +320,39 @@ path without file extension
.IP {/.}
basename without file extension
.RE
If no placeholder is present, an implicit "{}" at the end is assumed.
Examples:
- Find all test_*.py files and open them in your favorite editor:
fd -g 'test_*.py' -X vim
Note that this executes a single "vim" process with all search results as arguments.
- Find all *.rs files and count the lines with "wc -l ...":
fd -e rs -X wc -l
.RE
.SH PATTERN SYNTAX
The regular expression syntax used by fd is documented here:
.UR https://docs.rs/regex/1.0.0/regex/#syntax
.UE
https://docs.rs/regex/1.0.0/regex/#syntax
The glob syntax is documented here:
.UR https://docs.rs/globset/#syntax
.UE
https://docs.rs/globset/#syntax
.SH ENVIRONMENT
.TP
.B LS_COLORS
Determines how to colorize search results, see
.BR dircolors (1) .
.TP
.B NO_COLOR
Disables colorized output.
.TP
.B XDG_CONFIG_HOME, HOME
Used to locate the global ignore file. If
.B XDG_CONFIG_HOME
@@ -297,7 +368,7 @@ $ fd needle
.RI "Start a search in a given directory (" /var/log "):"
$ fd nginx /var/log
.TP
.RI "Find all Python files (all files with the extention " .py ") in the current directory:"
.RI "Find all Python files (all files with the extension " .py ") in the current directory:"
$ fd -e py
.TP
.RI "Open all search results with vim:"
+69 -26
View File
@@ -1,10 +1,16 @@
use clap::{crate_version, App, AppSettings, Arg};
pub fn build_app() -> App<'static, 'static> {
let clap_color_setting = if std::env::var_os("NO_COLOR").is_none() {
AppSettings::ColoredHelp
} else {
AppSettings::ColorNever
};
let mut app = App::new("fd")
.version(crate_version!())
.usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]")
.setting(AppSettings::ColoredHelp)
.setting(clap_color_setting)
.setting(AppSettings::DeriveDisplayOrder)
.after_help(
"Note: `fd -h` prints a short and concise overview while `fd --help` gives all \
@@ -111,7 +117,9 @@ pub fn build_app() -> App<'static, 'static> {
.overrides_with("fixed-strings")
.hidden_short_help(true)
.long_help(
"Treat the pattern as a literal string instead of a regular expression.",
"Treat the pattern as a literal string instead of a regular expression. Note \
that this also performs substring comparison. If you want to match on an \
exact filename, consider using '--glob'.",
),
)
.arg(
@@ -167,6 +175,7 @@ pub fn build_app() -> App<'static, 'static> {
.short("0")
.overrides_with("print0")
.conflicts_with("list-details")
.hidden_short_help(true)
.help("Separate results by the null character")
.long_help(
"Separate search results by the null character (instead of newlines). \
@@ -215,6 +224,13 @@ pub fn build_app() -> App<'static, 'static> {
'--min-depth <depth> --max-depth <depth>'.",
),
)
.arg(
Arg::with_name("prune")
.long("prune")
.conflicts_with_all(&["size", "exact-depth"])
.hidden_short_help(true)
.long_help("Do not traverse into matching directories.")
)
.arg(
Arg::with_name("file-type")
.long("type")
@@ -266,7 +282,9 @@ pub fn build_app() -> App<'static, 'static> {
.help("Filter by file extension")
.long_help(
"(Additionally) filter search results by their file extension. Multiple \
allowable file extensions can be specified.",
allowable file extensions can be specified.\n\
If you want to search for files without extension, \
you can use the regex '^[^.]+$' as a normal search pattern.",
),
)
.arg(
@@ -280,16 +298,24 @@ pub fn build_app() -> App<'static, 'static> {
.conflicts_with("list-details")
.help("Execute a command for each search result")
.long_help(
"Execute a command for each search result.\n\
All arguments following --exec are taken to be arguments to the command until the \
argument ';' is encountered.\n\
Each occurrence of the following placeholders is substituted by a path derived from the \
current search result before the command is executed:\n \
'{}': path\n \
"Execute a command for each search result in parallel (use --threads=1 for sequential command execution). \
All positional arguments following --exec are considered to be arguments to the command - not to fd. \
It is therefore recommended to place the '-x'/'--exec' option last.\n\
The following placeholders are substituted before the command is executed:\n \
'{}': path (of the current search result)\n \
'{/}': basename\n \
'{//}': parent directory\n \
'{.}': path without file extension\n \
'{/.}': basename without file extension",
'{/.}': basename without file extension\n\n\
If no placeholder is present, an implicit \"{}\" at the end is assumed.\n\n\
Examples:\n\n \
- find all *.zip files and unzip them:\n\n \
fd -e zip -x unzip\n\n \
- find *.h and *.cpp files and run \"clang-format -i ..\" for each of them:\n\n \
fd -e h -e cpp -x clang-format -i\n\n \
- Convert all *.jpg files to *.png files:\n\n \
fd -e jpg -x convert {} {.}.png\
",
),
)
.arg(
@@ -303,16 +329,20 @@ pub fn build_app() -> App<'static, 'static> {
.conflicts_with_all(&["exec", "list-details"])
.help("Execute a command with all search results at once")
.long_help(
"Execute a command with all search results at once.\n\
All arguments following --exec-batch are taken to be arguments to the command until the \
argument ';' is encountered.\n\
A single occurrence of the following placeholders is authorized and substituted by the paths derived from the \
search results before the command is executed:\n \
'{}': path\n \
"Execute the given command once, with all search results as arguments.\n\
One of the following placeholders is substituted before the command is executed:\n \
'{}': path (of all search results)\n \
'{/}': basename\n \
'{//}': parent directory\n \
'{.}': path without file extension\n \
'{/.}': basename without file extension",
'{/.}': basename without file extension\n\n\
If no placeholder is present, an implicit \"{}\" at the end is assumed.\n\n\
Examples:\n\n \
- Find all test_*.py files and open them in your favorite editor:\n\n \
fd -g 'test_*.py' -X vim\n\n \
- Find all *.rs files and count the lines with \"wc -l ...\":\n\n \
fd -e rs -X wc -l\
"
),
)
.arg(
@@ -327,7 +357,10 @@ pub fn build_app() -> App<'static, 'static> {
.long_help(
"Exclude files/directories that match the given glob pattern. This \
overrides any other ignore logic. Multiple exclude patterns can be \
specified.",
specified.\n\n\
Examples:\n \
--exclude '*.pyc'\n \
--exclude node_modules",
),
)
.arg(
@@ -383,7 +416,8 @@ pub fn build_app() -> App<'static, 'static> {
.long_help(
"Limit results based on the size of files using the format <+-><NUM><UNIT>.\n \
'+': file size must be greater than or equal to this\n \
'-': file size must be less than or equal to this\n \
'-': file size must be less than or equal to this\n\
If neither '+' nor '-' is specified, file size must be exactly equal to this.\n \
'NUM': The numeric size (e.g. 500)\n \
'UNIT': The units for NUM. They are not case-sensitive.\n\
Allowed unit values:\n \
@@ -412,6 +446,7 @@ pub fn build_app() -> App<'static, 'static> {
Arg::with_name("changed-within")
.long("changed-within")
.alias("change-newer-than")
.alias("newer")
.takes_value(true)
.value_name("date|dur")
.number_of_values(1)
@@ -419,16 +454,19 @@ pub fn build_app() -> App<'static, 'static> {
.long_help(
"Filter results based on the file modification time. The argument can be provided \
as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
'--change-newer-than' can be used as an alias.\n\
If the time is not specified, it defaults to 00:00:00. \
'--change-newer-than' or '--newer' can be used as aliases.\n\
Examples:\n \
--changed-within 2weeks\n \
--change-newer-than '2018-10-27 10:00:00'",
--change-newer-than '2018-10-27 10:00:00'\n \
--newer 2018-10-27",
),
)
.arg(
Arg::with_name("changed-before")
.long("changed-before")
.alias("change-older-than")
.alias("older")
.takes_value(true)
.value_name("date|dur")
.number_of_values(1)
@@ -436,10 +474,11 @@ pub fn build_app() -> App<'static, 'static> {
.long_help(
"Filter results based on the file modification time. The argument can be provided \
as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
'--change-older-than' can be used as an alias.\n\
'--change-older-than' or '--older' can be used as aliases.\n\
Examples:\n \
--changed-before '2018-10-27 10:00:00'\n \
--change-older-than 2weeks",
--change-older-than 2weeks\n \
--older 2018-10-27",
),
)
.arg(
@@ -483,7 +522,7 @@ pub fn build_app() -> App<'static, 'static> {
.number_of_values(1)
.hidden_short_help(true)
.long_help(
"Change the current working directory of fd to the provided path. The \
"Change the current working directory of fd to the provided path. This \
means that search results will be shown with respect to the given base \
path. Note that relative paths which are passed to fd via the positional \
<path> argument or the '--search-path' option will also be resolved \
@@ -492,8 +531,12 @@ pub fn build_app() -> App<'static, 'static> {
)
.arg(
Arg::with_name("pattern").help(
"the search pattern - a regular expression unless '--glob' is used (optional)",
),
"the search pattern (a regular expression, unless '--glob' is used; optional)",
).long_help(
"the search pattern which is either a regular expression (default) or a glob \
pattern (if --glob is used). If no pattern has been specified, every entry \
is considered a match. If your pattern starts with a dash (-), make sure to \
pass '--' first, or it will be considered as a flag (fd -- '-foo').")
)
.arg(
Arg::with_name("path-separator")
+12 -2
View File
@@ -7,9 +7,19 @@ use crate::error::print_error;
use crate::exit_codes::ExitCode;
/// Executes a command.
pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode {
pub fn execute_command(
mut cmd: Command,
out_perm: &Mutex<()>,
enable_output_buffering: bool,
) -> ExitCode {
// Spawn the supplied command.
let output = cmd.output();
let output = if enable_output_buffering {
cmd.output()
} else {
// If running on only one thread, don't buffer output
// Allows for viewing and interacting with intermediate command output
cmd.spawn().and_then(|c| c.wait_with_output())
};
// Then wait for the command to exit, if it was spawned.
match output {
+4 -2
View File
@@ -16,6 +16,7 @@ pub fn job(
cmd: Arc<CommandTemplate>,
out_perm: Arc<Mutex<()>>,
show_filesystem_errors: bool,
buffer_output: bool,
) -> ExitCode {
let mut results: Vec<ExitCode> = Vec::new();
loop {
@@ -38,7 +39,7 @@ pub fn job(
// Drop the lock so that other threads can read from the receiver.
drop(lock);
// Generate a command, execute it and store its exit code.
results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm)))
results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm), buffer_output))
}
// Returns error in case of any error.
merge_exitcodes(&results)
@@ -48,6 +49,7 @@ pub fn batch(
rx: Receiver<WorkerResult>,
cmd: &CommandTemplate,
show_filesystem_errors: bool,
buffer_output: bool,
) -> ExitCode {
let paths = rx.iter().filter_map(|value| match value {
WorkerResult::Entry(val) => Some(val),
@@ -58,5 +60,5 @@ pub fn batch(
None
}
});
cmd.generate_and_execute_batch(paths)
cmd.generate_and_execute_batch(paths, buffer_output)
}
+158 -36
View File
@@ -3,8 +3,9 @@ mod input;
mod job;
mod token;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::path::{Component, Path, PathBuf, Prefix};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};
@@ -37,23 +38,24 @@ pub enum ExecutionMode {
pub struct CommandTemplate {
args: Vec<ArgumentTemplate>,
mode: ExecutionMode,
path_separator: Option<String>,
}
impl CommandTemplate {
pub fn new<I, S>(input: I) -> CommandTemplate
pub fn new<I, S>(input: I, path_separator: Option<String>) -> CommandTemplate
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
Self::build(input, ExecutionMode::OneByOne)
Self::build(input, ExecutionMode::OneByOne, path_separator)
}
pub fn new_batch<I, S>(input: I) -> Result<CommandTemplate>
pub fn new_batch<I, S>(input: I, path_separator: Option<String>) -> Result<CommandTemplate>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let cmd = Self::build(input, ExecutionMode::Batch);
let cmd = Self::build(input, ExecutionMode::Batch, path_separator);
if cmd.number_of_tokens() > 1 {
return Err(anyhow!("Only one placeholder allowed for batch commands"));
}
@@ -65,7 +67,7 @@ impl CommandTemplate {
Ok(cmd)
}
fn build<I, S>(input: I, mode: ExecutionMode) -> CommandTemplate
fn build<I, S>(input: I, mode: ExecutionMode, path_separator: Option<String>) -> CommandTemplate
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
@@ -122,7 +124,11 @@ impl CommandTemplate {
args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
}
CommandTemplate { args, mode }
CommandTemplate {
args,
mode,
path_separator,
}
}
fn number_of_tokens(&self) -> usize {
@@ -133,26 +139,31 @@ impl CommandTemplate {
///
/// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
/// build. Once all arguments have been processed, the command is executed.
pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) -> ExitCode {
pub fn generate_and_execute(
&self,
input: &Path,
out_perm: Arc<Mutex<()>>,
buffer_output: bool,
) -> ExitCode {
let input = strip_current_dir(input);
let mut cmd = Command::new(self.args[0].generate(&input));
let mut cmd = Command::new(self.args[0].generate(&input, self.path_separator.as_deref()));
for arg in &self.args[1..] {
cmd.arg(arg.generate(&input));
cmd.arg(arg.generate(&input, self.path_separator.as_deref()));
}
execute_command(cmd, &out_perm)
execute_command(cmd, &out_perm, buffer_output)
}
pub fn in_batch_mode(&self) -> bool {
self.mode == ExecutionMode::Batch
}
pub fn generate_and_execute_batch<I>(&self, paths: I) -> ExitCode
pub fn generate_and_execute_batch<I>(&self, paths: I, buffer_output: bool) -> ExitCode
where
I: Iterator<Item = PathBuf>,
{
let mut cmd = Command::new(self.args[0].generate(""));
let mut cmd = Command::new(self.args[0].generate("", None));
cmd.stdin(Stdio::inherit());
cmd.stdout(Stdio::inherit());
cmd.stderr(Stdio::inherit());
@@ -167,16 +178,16 @@ impl CommandTemplate {
// A single `Tokens` is expected
// So we can directly consume the iterator once and for all
for path in &mut paths {
cmd.arg(arg.generate(strip_current_dir(path)));
cmd.arg(arg.generate(strip_current_dir(path), self.path_separator.as_deref()));
has_path = true;
}
} else {
cmd.arg(arg.generate(""));
cmd.arg(arg.generate("", None));
}
}
if has_path {
execute_command(cmd, &Mutex::new(()))
execute_command(cmd, &Mutex::new(()), buffer_output)
} else {
ExitCode::Success
}
@@ -195,27 +206,34 @@ enum ArgumentTemplate {
impl ArgumentTemplate {
pub fn has_tokens(&self) -> bool {
match self {
ArgumentTemplate::Tokens(_) => true,
_ => false,
}
matches!(self, ArgumentTemplate::Tokens(_))
}
pub fn generate(&self, path: impl AsRef<Path>) -> OsString {
/// Generate an argument from this template. If path_separator is Some, then it will replace
/// the path separator in all placeholder tokens. Text arguments and tokens are not affected by
/// path separator substitution.
pub fn generate(&self, path: impl AsRef<Path>, path_separator: Option<&str>) -> OsString {
use self::Token::*;
let path = path.as_ref();
match *self {
ArgumentTemplate::Tokens(ref tokens) => {
let mut s = OsString::new();
for token in tokens {
match *token {
Basename => s.push(basename(path.as_ref())),
BasenameNoExt => {
s.push(remove_extension(&PathBuf::from(basename(path.as_ref()))))
Basename => s.push(Self::replace_separator(basename(path), path_separator)),
BasenameNoExt => s.push(Self::replace_separator(
&remove_extension(basename(path).as_ref()),
path_separator,
)),
NoExt => s.push(Self::replace_separator(
&remove_extension(path),
path_separator,
)),
Parent => s.push(Self::replace_separator(&dirname(path), path_separator)),
Placeholder => {
s.push(Self::replace_separator(path.as_ref(), path_separator))
}
NoExt => s.push(remove_extension(path.as_ref())),
Parent => s.push(dirname(path.as_ref())),
Placeholder => s.push(path.as_ref()),
Text(ref string) => s.push(string),
}
}
@@ -224,6 +242,61 @@ impl ArgumentTemplate {
ArgumentTemplate::Text(ref text) => OsString::from(text),
}
}
/// Replace the path separator in the input with the custom separator string. If path_separator
/// is None, simply return a borrowed Cow<OsStr> of the input. Otherwise, the input is
/// interpreted as a Path and its components are iterated through and re-joined into a new
/// OsString.
fn replace_separator<'a>(path: &'a OsStr, path_separator: Option<&str>) -> Cow<'a, OsStr> {
// fast-path - no replacement necessary
if path_separator.is_none() {
return Cow::Borrowed(path);
}
let path_separator = path_separator.unwrap();
let mut out = OsString::with_capacity(path.len());
let mut components = Path::new(path).components().peekable();
while let Some(comp) = components.next() {
match comp {
// Absolute paths on Windows are tricky. A Prefix component is usually a drive
// letter or UNC path, and is usually followed by RootDir. There are also
// "verbatim" prefixes beginning with "\\?\" that skip normalization. We choose to
// ignore verbatim path prefixes here because they're very rare, might be
// impossible to reach here, and there's no good way to deal with them. If users
// are doing something advanced involving verbatim windows paths, they can do their
// own output filtering with a tool like sed.
Component::Prefix(prefix) => {
if let Prefix::UNC(server, share) = prefix.kind() {
// Prefix::UNC is a parsed version of '\\server\share'
out.push(path_separator);
out.push(path_separator);
out.push(server);
out.push(path_separator);
out.push(share);
} else {
// All other Windows prefix types are rendered as-is. This results in e.g. "C:" for
// drive letters. DeviceNS and Verbatim* prefixes won't have backslashes converted,
// but they're not returned by directories fd can search anyway so we don't worry
// about them.
out.push(comp.as_os_str());
}
}
// Root directory is always replaced with the custom separator.
Component::RootDir => out.push(path_separator),
// Everything else is joined normally, with a trailing separator if we're not last
_ => {
out.push(comp.as_os_str());
if components.peek().is_some() {
out.push(path_separator);
}
}
}
}
Cow::Owned(out)
}
}
#[cfg(test)]
@@ -233,7 +306,7 @@ mod tests {
#[test]
fn tokens_with_placeholder() {
assert_eq!(
CommandTemplate::new(&[&"echo", &"${SHELL}:"]),
CommandTemplate::new(&[&"echo", &"${SHELL}:"], None),
CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
@@ -241,6 +314,7 @@ mod tests {
ArgumentTemplate::Tokens(vec![Token::Placeholder]),
],
mode: ExecutionMode::OneByOne,
path_separator: None,
}
);
}
@@ -248,13 +322,14 @@ mod tests {
#[test]
fn tokens_with_no_extension() {
assert_eq!(
CommandTemplate::new(&["echo", "{.}"]),
CommandTemplate::new(&["echo", "{.}"], None),
CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::NoExt]),
],
mode: ExecutionMode::OneByOne,
path_separator: None,
}
);
}
@@ -262,13 +337,14 @@ mod tests {
#[test]
fn tokens_with_basename() {
assert_eq!(
CommandTemplate::new(&["echo", "{/}"]),
CommandTemplate::new(&["echo", "{/}"], None),
CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::Basename]),
],
mode: ExecutionMode::OneByOne,
path_separator: None,
}
);
}
@@ -276,13 +352,14 @@ mod tests {
#[test]
fn tokens_with_parent() {
assert_eq!(
CommandTemplate::new(&["echo", "{//}"]),
CommandTemplate::new(&["echo", "{//}"], None),
CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::Parent]),
],
mode: ExecutionMode::OneByOne,
path_separator: None,
}
);
}
@@ -290,13 +367,14 @@ mod tests {
#[test]
fn tokens_with_basename_no_extension() {
assert_eq!(
CommandTemplate::new(&["echo", "{/.}"]),
CommandTemplate::new(&["echo", "{/.}"], None),
CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
],
mode: ExecutionMode::OneByOne,
path_separator: None,
}
);
}
@@ -304,7 +382,7 @@ mod tests {
#[test]
fn tokens_multiple() {
assert_eq!(
CommandTemplate::new(&["cp", "{}", "{/.}.ext"]),
CommandTemplate::new(&["cp", "{}", "{/.}.ext"], None),
CommandTemplate {
args: vec![
ArgumentTemplate::Text("cp".into()),
@@ -315,6 +393,7 @@ mod tests {
]),
],
mode: ExecutionMode::OneByOne,
path_separator: None,
}
);
}
@@ -322,19 +401,62 @@ mod tests {
#[test]
fn tokens_single_batch() {
assert_eq!(
CommandTemplate::new_batch(&["echo", "{.}"]).unwrap(),
CommandTemplate::new_batch(&["echo", "{.}"], None).unwrap(),
CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::NoExt]),
],
mode: ExecutionMode::Batch,
path_separator: None,
}
);
}
#[test]
fn tokens_multiple_batch() {
assert!(CommandTemplate::new_batch(&["echo", "{.}", "{}"]).is_err());
assert!(CommandTemplate::new_batch(&["echo", "{.}", "{}"], None).is_err());
}
#[test]
fn generate_custom_path_separator() {
let arg = ArgumentTemplate::Tokens(vec![Token::Placeholder]);
macro_rules! check {
($input:expr, $expected:expr) => {
assert_eq!(arg.generate($input, Some("#")), OsString::from($expected));
};
}
check!("foo", "foo");
check!("foo/bar", "foo#bar");
check!("/foo/bar/baz", "#foo#bar#baz");
}
#[cfg(windows)]
#[test]
fn generate_custom_path_separator_windows() {
let arg = ArgumentTemplate::Tokens(vec![Token::Placeholder]);
macro_rules! check {
($input:expr, $expected:expr) => {
assert_eq!(arg.generate($input, Some("#")), OsString::from($expected));
};
}
// path starting with a drive letter
check!(r"C:\foo\bar", "C:#foo#bar");
// UNC path
check!(r"\\server\share\path", "##server#share#path");
// Drive Relative path - no separator after the colon omits the RootDir path component.
// This is uncommon, but valid
check!(r"C:foo\bar", "C:foo#bar");
// forward slashses should get normalized and interpreted as separators
check!("C:/foo/bar", "C:#foo#bar");
check!("C:foo/bar", "C:foo#bar");
// Rust does not intrepret "//server/share" as a UNC path, but rather as a normal
// absolute path that begins with RootDir, and the two slashes get combined together as
// a single path separator during normalization.
//check!("//server/share/path", "##server#share#path");
}
}
+6 -6
View File
@@ -5,9 +5,9 @@ pub enum ExitCode {
KilledBySigint,
}
impl Into<i32> for ExitCode {
fn into(self) -> i32 {
match self {
impl From<ExitCode> for i32 {
fn from(code: ExitCode) -> Self {
match code {
ExitCode::Success => 0,
ExitCode::GeneralError => 1,
ExitCode::KilledBySigint => 130,
@@ -16,13 +16,13 @@ impl Into<i32> for ExitCode {
}
impl ExitCode {
fn is_error(&self) -> bool {
*self != ExitCode::Success
fn is_error(self) -> bool {
self != ExitCode::Success
}
}
pub fn merge_exitcodes(results: &[ExitCode]) -> ExitCode {
if results.iter().any(ExitCode::is_error) {
if results.iter().any(|&c| ExitCode::is_error(c)) {
return ExitCode::GeneralError;
}
ExitCode::Success
+29 -10
View File
@@ -1,5 +1,5 @@
use std::borrow::Cow;
use std::env::current_dir;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io;
@@ -7,6 +7,8 @@ use std::io;
use std::os::unix::fs::{FileTypeExt, PermissionsExt};
use std::path::{Path, PathBuf};
use normpath::PathExt;
use crate::walk;
pub fn path_absolute_form(path: &Path) -> io::Result<PathBuf> {
@@ -15,7 +17,7 @@ pub fn path_absolute_form(path: &Path) -> io::Result<PathBuf> {
}
let path = path.strip_prefix(".").unwrap_or(path);
current_dir().map(|path_buf| path_buf.join(path))
env::current_dir().map(|path_buf| path_buf.join(path))
}
pub fn absolute_path(path: &Path) -> io::Result<PathBuf> {
@@ -33,10 +35,10 @@ pub fn absolute_path(path: &Path) -> io::Result<PathBuf> {
Ok(path_buf)
}
// Path::is_dir() is not guaranteed to be intuitively correct for "." and ".."
// See: https://github.com/rust-lang/rust/issues/45302
pub fn is_dir(path: &Path) -> bool {
path.is_dir() && (path.file_name().is_some() || path.canonicalize().is_ok())
pub fn is_existing_directory(path: &Path) -> bool {
// Note: we do not use `.exists()` here, as `.` always exists, even if
// the CWD has been deleted.
path.is_dir() && (path.file_name().is_some() || path.normalize().is_ok())
}
#[cfg(any(unix, target_os = "redox"))]
@@ -68,22 +70,22 @@ pub fn is_empty(entry: &walk::DirEntry) -> bool {
}
#[cfg(any(unix, target_os = "redox"))]
pub fn is_socket(ft: &fs::FileType) -> bool {
pub fn is_socket(ft: fs::FileType) -> bool {
ft.is_socket()
}
#[cfg(windows)]
pub fn is_socket(_: &fs::FileType) -> bool {
pub fn is_socket(_: fs::FileType) -> bool {
false
}
#[cfg(any(unix, target_os = "redox"))]
pub fn is_pipe(ft: &fs::FileType) -> bool {
pub fn is_pipe(ft: fs::FileType) -> bool {
ft.is_fifo()
}
#[cfg(windows)]
pub fn is_pipe(_: &fs::FileType) -> bool {
pub fn is_pipe(_: fs::FileType) -> bool {
false
}
@@ -108,6 +110,23 @@ pub fn strip_current_dir(path: &Path) -> &Path {
path.strip_prefix(".").unwrap_or(path)
}
/// Default value for the path_separator, mainly for MSYS/MSYS2, which set the MSYSTEM
/// environment variable, and we set fd's path separator to '/' rather than Rust's default of '\'.
///
/// Returns Some to use a nonstandard path separator, or None to use rust's default on the target
/// platform.
pub fn default_path_separator() -> Option<String> {
if cfg!(windows) {
let msystem = env::var("MSYSTEM").ok()?;
match msystem.as_str() {
"MINGW64" | "MINGW32" | "MSYS" => Some("/".to_owned()),
_ => None,
}
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::strip_current_dir;
+60 -57
View File
@@ -2,13 +2,14 @@ use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref SIZE_CAPTURES: Regex = Regex::new(r"(?i)^([+-])(\d+)(b|[kmgt]i?b?)$").unwrap();
static ref SIZE_CAPTURES: Regex = Regex::new(r"(?i)^([+-]?)(\d+)(b|[kmgt]i?b?)$").unwrap();
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SizeFilter {
Max(u64),
Min(u64),
Equals(u64),
}
// SI prefixes (powers of 10)
@@ -49,16 +50,19 @@ impl SizeFilter {
};
let size = quantity * multiplier;
Some(match limit_kind {
"+" => SizeFilter::Min(size),
_ => SizeFilter::Max(size),
})
match limit_kind {
"+" => Some(SizeFilter::Min(size)),
"-" => Some(SizeFilter::Max(size)),
"" => Some(SizeFilter::Equals(size)),
_ => None,
}
}
pub fn is_within(&self, size: u64) -> bool {
match *self {
SizeFilter::Max(limit) => size <= limit,
SizeFilter::Min(limit) => size >= limit,
SizeFilter::Equals(limit) => size == limit,
}
}
}
@@ -89,70 +93,70 @@ mod tests {
kilo_plus: ("+1k", SizeFilter::Min(1000)),
kilo_plus_suffix: ("+1kb", SizeFilter::Min(1000)),
kilo_minus: ("-1k", SizeFilter::Max(1000)),
kilo_minus_multiplier: ("-100k", SizeFilter::Max(100000)),
kilo_minus_multiplier: ("-100k", SizeFilter::Max(100_000)),
kilo_minus_suffix: ("-1kb", SizeFilter::Max(1000)),
kilo_plus_upper: ("+1K", SizeFilter::Min(1000)),
kilo_plus_suffix_upper: ("+1KB", SizeFilter::Min(1000)),
kilo_minus_upper: ("-1K", SizeFilter::Max(1000)),
kilo_minus_suffix_upper: ("-1Kb", SizeFilter::Max(1000)),
kibi_plus: ("+1ki", SizeFilter::Min(1024)),
kibi_plus_multiplier: ("+10ki", SizeFilter::Min(10240)),
kibi_plus_multiplier: ("+10ki", SizeFilter::Min(10_240)),
kibi_plus_suffix: ("+1kib", SizeFilter::Min(1024)),
kibi_minus: ("-1ki", SizeFilter::Max(1024)),
kibi_minus_multiplier: ("-100ki", SizeFilter::Max(102400)),
kibi_minus_multiplier: ("-100ki", SizeFilter::Max(102_400)),
kibi_minus_suffix: ("-1kib", SizeFilter::Max(1024)),
kibi_plus_upper: ("+1KI", SizeFilter::Min(1024)),
kibi_plus_suffix_upper: ("+1KiB", SizeFilter::Min(1024)),
kibi_minus_upper: ("-1Ki", SizeFilter::Max(1024)),
kibi_minus_suffix_upper: ("-1KIB", SizeFilter::Max(1024)),
mega_plus: ("+1m", SizeFilter::Min(1000000)),
mega_plus_suffix: ("+1mb", SizeFilter::Min(1000000)),
mega_minus: ("-1m", SizeFilter::Max(1000000)),
mega_minus_suffix: ("-1mb", SizeFilter::Max(1000000)),
mega_plus_upper: ("+1M", SizeFilter::Min(1000000)),
mega_plus_suffix_upper: ("+1MB", SizeFilter::Min(1000000)),
mega_minus_upper: ("-1M", SizeFilter::Max(1000000)),
mega_minus_suffix_upper: ("-1Mb", SizeFilter::Max(1000000)),
mebi_plus: ("+1mi", SizeFilter::Min(1048576)),
mebi_plus_suffix: ("+1mib", SizeFilter::Min(1048576)),
mebi_minus: ("-1mi", SizeFilter::Max(1048576)),
mebi_minus_suffix: ("-1mib", SizeFilter::Max(1048576)),
mebi_plus_upper: ("+1MI", SizeFilter::Min(1048576)),
mebi_plus_suffix_upper: ("+1MiB", SizeFilter::Min(1048576)),
mebi_minus_upper: ("-1Mi", SizeFilter::Max(1048576)),
mebi_minus_suffix_upper: ("-1MIB", SizeFilter::Max(1048576)),
giga_plus: ("+1g", SizeFilter::Min(1000000000)),
giga_plus_suffix: ("+1gb", SizeFilter::Min(1000000000)),
giga_minus: ("-1g", SizeFilter::Max(1000000000)),
giga_minus_suffix: ("-1gb", SizeFilter::Max(1000000000)),
giga_plus_upper: ("+1G", SizeFilter::Min(1000000000)),
giga_plus_suffix_upper: ("+1GB", SizeFilter::Min(1000000000)),
giga_minus_upper: ("-1G", SizeFilter::Max(1000000000)),
giga_minus_suffix_upper: ("-1Gb", SizeFilter::Max(1000000000)),
gibi_plus: ("+1gi", SizeFilter::Min(1073741824)),
gibi_plus_suffix: ("+1gib", SizeFilter::Min(1073741824)),
gibi_minus: ("-1gi", SizeFilter::Max(1073741824)),
gibi_minus_suffix: ("-1gib", SizeFilter::Max(1073741824)),
gibi_plus_upper: ("+1GI", SizeFilter::Min(1073741824)),
gibi_plus_suffix_upper: ("+1GiB", SizeFilter::Min(1073741824)),
gibi_minus_upper: ("-1Gi", SizeFilter::Max(1073741824)),
gibi_minus_suffix_upper: ("-1GIB", SizeFilter::Max(1073741824)),
tera_plus: ("+1t", SizeFilter::Min(1000000000000)),
tera_plus_suffix: ("+1tb", SizeFilter::Min(1000000000000)),
tera_minus: ("-1t", SizeFilter::Max(1000000000000)),
tera_minus_suffix: ("-1tb", SizeFilter::Max(1000000000000)),
tera_plus_upper: ("+1T", SizeFilter::Min(1000000000000)),
tera_plus_suffix_upper: ("+1TB", SizeFilter::Min(1000000000000)),
tera_minus_upper: ("-1T", SizeFilter::Max(1000000000000)),
tera_minus_suffix_upper: ("-1Tb", SizeFilter::Max(1000000000000)),
tebi_plus: ("+1ti", SizeFilter::Min(1099511627776)),
tebi_plus_suffix: ("+1tib", SizeFilter::Min(1099511627776)),
tebi_minus: ("-1ti", SizeFilter::Max(1099511627776)),
tebi_minus_suffix: ("-1tib", SizeFilter::Max(1099511627776)),
tebi_plus_upper: ("+1TI", SizeFilter::Min(1099511627776)),
tebi_plus_suffix_upper: ("+1TiB", SizeFilter::Min(1099511627776)),
tebi_minus_upper: ("-1Ti", SizeFilter::Max(1099511627776)),
tebi_minus_suffix_upper: ("-1TIB", SizeFilter::Max(1099511627776)),
mega_plus: ("+1m", SizeFilter::Min(1_000_000)),
mega_plus_suffix: ("+1mb", SizeFilter::Min(1_000_000)),
mega_minus: ("-1m", SizeFilter::Max(1_000_000)),
mega_minus_suffix: ("-1mb", SizeFilter::Max(1_000_000)),
mega_plus_upper: ("+1M", SizeFilter::Min(1_000_000)),
mega_plus_suffix_upper: ("+1MB", SizeFilter::Min(1_000_000)),
mega_minus_upper: ("-1M", SizeFilter::Max(1_000_000)),
mega_minus_suffix_upper: ("-1Mb", SizeFilter::Max(1_000_000)),
mebi_plus: ("+1mi", SizeFilter::Min(1_048_576)),
mebi_plus_suffix: ("+1mib", SizeFilter::Min(1_048_576)),
mebi_minus: ("-1mi", SizeFilter::Max(1_048_576)),
mebi_minus_suffix: ("-1mib", SizeFilter::Max(1_048_576)),
mebi_plus_upper: ("+1MI", SizeFilter::Min(1_048_576)),
mebi_plus_suffix_upper: ("+1MiB", SizeFilter::Min(1_048_576)),
mebi_minus_upper: ("-1Mi", SizeFilter::Max(1_048_576)),
mebi_minus_suffix_upper: ("-1MIB", SizeFilter::Max(1_048_576)),
giga_plus: ("+1g", SizeFilter::Min(1_000_000_000)),
giga_plus_suffix: ("+1gb", SizeFilter::Min(1_000_000_000)),
giga_minus: ("-1g", SizeFilter::Max(1_000_000_000)),
giga_minus_suffix: ("-1gb", SizeFilter::Max(1_000_000_000)),
giga_plus_upper: ("+1G", SizeFilter::Min(1_000_000_000)),
giga_plus_suffix_upper: ("+1GB", SizeFilter::Min(1_000_000_000)),
giga_minus_upper: ("-1G", SizeFilter::Max(1_000_000_000)),
giga_minus_suffix_upper: ("-1Gb", SizeFilter::Max(1_000_000_000)),
gibi_plus: ("+1gi", SizeFilter::Min(1_073_741_824)),
gibi_plus_suffix: ("+1gib", SizeFilter::Min(1_073_741_824)),
gibi_minus: ("-1gi", SizeFilter::Max(1_073_741_824)),
gibi_minus_suffix: ("-1gib", SizeFilter::Max(1_073_741_824)),
gibi_plus_upper: ("+1GI", SizeFilter::Min(1_073_741_824)),
gibi_plus_suffix_upper: ("+1GiB", SizeFilter::Min(1_073_741_824)),
gibi_minus_upper: ("-1Gi", SizeFilter::Max(1_073_741_824)),
gibi_minus_suffix_upper: ("-1GIB", SizeFilter::Max(1_073_741_824)),
tera_plus: ("+1t", SizeFilter::Min(1_000_000_000_000)),
tera_plus_suffix: ("+1tb", SizeFilter::Min(1_000_000_000_000)),
tera_minus: ("-1t", SizeFilter::Max(1_000_000_000_000)),
tera_minus_suffix: ("-1tb", SizeFilter::Max(1_000_000_000_000)),
tera_plus_upper: ("+1T", SizeFilter::Min(1_000_000_000_000)),
tera_plus_suffix_upper: ("+1TB", SizeFilter::Min(1_000_000_000_000)),
tera_minus_upper: ("-1T", SizeFilter::Max(1_000_000_000_000)),
tera_minus_suffix_upper: ("-1Tb", SizeFilter::Max(1_000_000_000_000)),
tebi_plus: ("+1ti", SizeFilter::Min(1_099_511_627_776)),
tebi_plus_suffix: ("+1tib", SizeFilter::Min(1_099_511_627_776)),
tebi_minus: ("-1ti", SizeFilter::Max(1_099_511_627_776)),
tebi_minus_suffix: ("-1tib", SizeFilter::Max(1_099_511_627_776)),
tebi_plus_upper: ("+1TI", SizeFilter::Min(1_099_511_627_776)),
tebi_plus_suffix_upper: ("+1TiB", SizeFilter::Min(1_099_511_627_776)),
tebi_minus_upper: ("-1Ti", SizeFilter::Max(1_099_511_627_776)),
tebi_minus_suffix_upper: ("-1TIB", SizeFilter::Max(1_099_511_627_776)),
}
/// Invalid parse testing
@@ -170,7 +174,6 @@ mod tests {
// Invalid parse data
gen_size_filter_failure! {
ensure_missing_symbol_returns_none: "10M",
ensure_missing_number_returns_none: "+g",
ensure_missing_unit_returns_none: "+18",
ensure_bad_format_returns_none_1: "$10M",
+1
View File
@@ -12,6 +12,7 @@ impl TimeFilter {
humantime::parse_duration(s)
.map(|duration| *ref_time - duration)
.or_else(|_| humantime::parse_rfc3339_weak(s))
.or_else(|_| humantime::parse_rfc3339_weak(&(s.to_owned() + " 00:00:00")))
.ok()
}
+88 -24
View File
@@ -21,6 +21,7 @@ use atty::Stream;
use globset::GlobBuilder;
use lscolors::LsColors;
use regex::bytes::{RegexBuilder, RegexSetBuilder};
use normpath::PathExt;
use crate::error::print_error;
use crate::exec::CommandTemplate;
@@ -30,21 +31,32 @@ use crate::filetypes::FileTypes;
use crate::filter::OwnerFilter;
use crate::filter::{SizeFilter, TimeFilter};
use crate::options::Options;
use crate::regex_helper::pattern_has_uppercase_char;
use crate::regex_helper::{pattern_has_uppercase_char, pattern_matches_strings_with_leading_dot};
// We use jemalloc for performance reasons, see https://github.com/sharkdp/fd/pull/481
// FIXME: re-enable jemalloc on macOS, see comment in Cargo.toml file for more infos
#[cfg(all(not(windows), not(target_os = "macos"), not(target_env = "musl")))]
#[cfg(all(
not(windows),
not(target_os = "android"),
not(target_os = "macos"),
not(target_env = "musl")
))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
// vivid --color-mode 8-bit generate molokai
const DEFAULT_LS_COLORS: &str = "
ow=0:or=0;38;5;16;48;5;203:no=0:ex=1;38;5;203:cd=0;38;5;203;48;5;236:mi=0;38;5;16;48;5;203:*~=0;38;5;243:st=0:pi=0;38;5;16;48;5;81:fi=0:di=0;38;5;81:so=0;38;5;16;48;5;203:bd=0;38;5;81;48;5;236:tw=0:ln=0;38;5;203:*.m=0;38;5;48:*.o=0;38;5;243:*.z=4;38;5;203:*.a=1;38;5;203:*.r=0;38;5;48:*.c=0;38;5;48:*.d=0;38;5;48:*.t=0;38;5;48:*.h=0;38;5;48:*.p=0;38;5;48:*.cc=0;38;5;48:*.ll=0;38;5;48:*.jl=0;38;5;48:*css=0;38;5;48:*.md=0;38;5;185:*.gz=4;38;5;203:*.nb=0;38;5;48:*.mn=0;38;5;48:*.go=0;38;5;48:*.xz=4;38;5;203:*.so=1;38;5;203:*.rb=0;38;5;48:*.pm=0;38;5;48:*.bc=0;38;5;243:*.py=0;38;5;48:*.as=0;38;5;48:*.pl=0;38;5;48:*.rs=0;38;5;48:*.sh=0;38;5;48:*.7z=4;38;5;203:*.ps=0;38;5;186:*.cs=0;38;5;48:*.el=0;38;5;48:*.rm=0;38;5;208:*.hs=0;38;5;48:*.td=0;38;5;48:*.ui=0;38;5;149:*.ex=0;38;5;48:*.js=0;38;5;48:*.cp=0;38;5;48:*.cr=0;38;5;48:*.la=0;38;5;243:*.kt=0;38;5;48:*.ml=0;38;5;48:*.vb=0;38;5;48:*.gv=0;38;5;48:*.lo=0;38;5;243:*.hi=0;38;5;243:*.ts=0;38;5;48:*.ko=1;38;5;203:*.hh=0;38;5;48:*.pp=0;38;5;48:*.di=0;38;5;48:*.bz=4;38;5;203:*.fs=0;38;5;48:*.png=0;38;5;208:*.zsh=0;38;5;48:*.mpg=0;38;5;208:*.pid=0;38;5;243:*.xmp=0;38;5;149:*.iso=4;38;5;203:*.m4v=0;38;5;208:*.dot=0;38;5;48:*.ods=0;38;5;186:*.inc=0;38;5;48:*.sxw=0;38;5;186:*.aif=0;38;5;208:*.git=0;38;5;243:*.gvy=0;38;5;48:*.tbz=4;38;5;203:*.log=0;38;5;243:*.txt=0;38;5;185:*.ico=0;38;5;208:*.csx=0;38;5;48:*.vob=0;38;5;208:*.pgm=0;38;5;208:*.pps=0;38;5;186:*.ics=0;38;5;186:*.img=4;38;5;203:*.fon=0;38;5;208:*.hpp=0;38;5;48:*.bsh=0;38;5;48:*.sql=0;38;5;48:*TODO=1:*.php=0;38;5;48:*.pkg=4;38;5;203:*.ps1=0;38;5;48:*.csv=0;38;5;185:*.ilg=0;38;5;243:*.ini=0;38;5;149:*.pyc=0;38;5;243:*.psd=0;38;5;208:*.htc=0;38;5;48:*.swp=0;38;5;243:*.mli=0;38;5;48:*hgrc=0;38;5;149:*.bst=0;38;5;149:*.ipp=0;38;5;48:*.fsi=0;38;5;48:*.tcl=0;38;5;48:*.exs=0;38;5;48:*.out=0;38;5;243:*.jar=4;38;5;203:*.xls=0;38;5;186:*.ppm=0;38;5;208:*.apk=4;38;5;203:*.aux=0;38;5;243:*.rpm=4;38;5;203:*.dll=1;38;5;203:*.eps=0;38;5;208:*.exe=1;38;5;203:*.doc=0;38;5;186:*.wma=0;38;5;208:*.deb=4;38;5;203:*.pod=0;38;5;48:*.ind=0;38;5;243:*.nix=0;38;5;149:*.lua=0;38;5;48:*.epp=0;38;5;48:*.dpr=0;38;5;48:*.htm=0;38;5;185:*.ogg=0;38;5;208:*.bin=4;38;5;203:*.otf=0;38;5;208:*.yml=0;38;5;149:*.pro=0;38;5;149:*.cxx=0;38;5;48:*.tex=0;38;5;48:*.fnt=0;38;5;208:*.erl=0;38;5;48:*.sty=0;38;5;243:*.bag=4;38;5;203:*.rst=0;38;5;185:*.pdf=0;38;5;186:*.pbm=0;38;5;208:*.xcf=0;38;5;208:*.clj=0;38;5;48:*.gif=0;38;5;208:*.rar=4;38;5;203:*.elm=0;38;5;48:*.bib=0;38;5;149:*.tsx=0;38;5;48:*.dmg=4;38;5;203:*.tmp=0;38;5;243:*.bcf=0;38;5;243:*.mkv=0;38;5;208:*.svg=0;38;5;208:*.cpp=0;38;5;48:*.vim=0;38;5;48:*.bmp=0;38;5;208:*.ltx=0;38;5;48:*.fls=0;38;5;243:*.flv=0;38;5;208:*.wav=0;38;5;208:*.m4a=0;38;5;208:*.mid=0;38;5;208:*.hxx=0;38;5;48:*.pas=0;38;5;48:*.wmv=0;38;5;208:*.tif=0;38;5;208:*.kex=0;38;5;186:*.mp4=0;38;5;208:*.bak=0;38;5;243:*.xlr=0;38;5;186:*.dox=0;38;5;149:*.swf=0;38;5;208:*.tar=4;38;5;203:*.tgz=4;38;5;203:*.cfg=0;38;5;149:*.xml=0;
38;5;185:*.jpg=0;38;5;208:*.mir=0;38;5;48:*.sxi=0;38;5;186:*.bz2=4;38;5;203:*.odt=0;38;5;186:*.mov=0;38;5;208:*.toc=0;38;5;243:*.bat=1;38;5;203:*.asa=0;38;5;48:*.awk=0;38;5;48:*.sbt=0;38;5;48:*.vcd=4;38;5;203:*.kts=0;38;5;48:*.arj=4;38;5;203:*.blg=0;38;5;243:*.c++=0;38;5;48:*.odp=0;38;5;186:*.bbl=0;38;5;243:*.idx=0;38;5;243:*.com=1;38;5;203:*.mp3=0;38;5;208:*.avi=0;38;5;208:*.def=0;38;5;48:*.cgi=0;38;5;48:*.zip=4;38;5;203:*.ttf=0;38;5;208:*.ppt=0;38;5;186:*.tml=0;38;5;149:*.fsx=0;38;5;48:*.h++=0;38;5;48:*.rtf=0;38;5;186:*.inl=0;38;5;48:*.yaml=0;38;5;149:*.html=0;38;5;185:*.mpeg=0;38;5;208:*.java=0;38;5;48:*.hgrc=0;38;5;149:*.orig=0;38;5;243:*.conf=0;38;5;149:*.dart=0;38;5;48:*.psm1=0;38;5;48:*.rlib=0;38;5;243:*.fish=0;38;5;48:*.bash=0;38;5;48:*.make=0;38;5;149:*.docx=0;38;5;186:*.json=0;38;5;149:*.psd1=0;38;5;48:*.lisp=0;38;5;48:*.tbz2=4;38;5;203:*.diff=0;38;5;48:*.epub=0;38;5;186:*.xlsx=0;38;5;186:*.pptx=0;38;5;186:*.toml=0;38;5;149:*.h264=0;38;5;208:*.purs=0;38;5;48:*.flac=0;38;5;208:*.tiff=0;38;5;208:*.jpeg=0;38;5;208:*.lock=0;38;5;243:*.less=0;38;5;48:*.dyn_o=0;38;5;243:*.scala=0;38;5;48:*.mdown=0;38;5;185:*.shtml=0;38;5;185:*.class=0;38;5;243:*.cache=0;38;5;243:*.cmake=0;38;5;149:*passwd=0;38;5;149:*.swift=0;38;5;48:*shadow=0;38;5;149:*.xhtml=0;38;5;185:*.patch=0;38;5;48:*.cabal=0;38;5;48:*README=0;38;5;16;48;5;186:*.toast=4;38;5;203:*.ipynb=0;38;5;48:*COPYING=0;38;5;249:*.gradle=0;38;5;48:*.matlab=0;38;5;48:*.config=0;38;5;149:*LICENSE=0;38;5;249:*.dyn_hi=0;38;5;243:*.flake8=0;38;5;149:*.groovy=0;38;5;48:*INSTALL=0;38;5;16;48;5;186:*TODO.md=1:*.ignore=0;38;5;149:*Doxyfile=0;38;5;149:*TODO.txt=1:*setup.py=0;38;5;149:*Makefile=0;38;5;149:*.gemspec=0;38;5;149:*.desktop=0;38;5;149:*.rgignore=0;38;5;149:*.markdown=0;38;5;185:*COPYRIGHT=0;38;5;249:*configure=0;38;5;149:*.DS_Store=0;38;5;243:*.kdevelop=0;38;5;149:*.fdignore=0;38;5;149:*README.md=0;38;5;16;48;5;186:*.cmake.in=0;38;5;149:*SConscript=0;38;5;149:*CODEOWNERS=0;38;5;149:*.localized=0;38;5;243:*.gitignore=0;38;5;149:*Dockerfile=0;38;5;149:*.gitconfig=0;38;5;149:*INSTALL.md=0;38;5;16;48;5;186:*README.txt=0;38;5;16;48;5;186:*SConstruct=0;38;5;149:*.scons_opt=0;38;5;243:*.travis.yml=0;38;5;186:*.gitmodules=0;38;5;149:*.synctex.gz=0;38;5;243:*LICENSE-MIT=0;38;5;249:*MANIFEST.in=0;38;5;149:*Makefile.in=0;38;5;243:*Makefile.am=0;38;5;149:*INSTALL.txt=0;38;5;16;48;5;186:*configure.ac=0;38;5;149:*.applescript=0;38;5;48:*appveyor.yml=0;38;5;186:*.fdb_latexmk=0;38;5;243:*CONTRIBUTORS=0;38;5;16;48;5;186:*.clang-format=0;38;5;149:*LICENSE-APACHE=0;38;5;249:*CMakeLists.txt=0;38;5;149:*CMakeCache.txt=0;38;5;243:*.gitattributes=0;38;5;149:*CONTRIBUTORS.md=0;38;5;16;48;5;186:*.sconsign.dblite=0;38;5;243:*requirements.txt=0;38;5;149:*CONTRIBUTORS.txt=0;38;5;16;48;5;186:*package-lock.json=0;38;5;243:*.CFUserTextEncoding=0;38;5;243
";
fn run() -> Result<ExitCode> {
let matches = app::build_app().get_matches_from(env::args_os());
// Set the current working directory of the process
if let Some(base_directory) = matches.value_of_os("base-directory") {
let base_directory = Path::new(base_directory);
if !filesystem::is_dir(base_directory) {
if !filesystem::is_existing_directory(base_directory) {
return Err(anyhow!(
"The '--base-directory' path '{}' is not a directory.",
base_directory.to_string_lossy()
@@ -52,14 +64,14 @@ fn run() -> Result<ExitCode> {
}
env::set_current_dir(base_directory).with_context(|| {
format!(
"Could not set '{}' as the current working directory.",
"Could not set '{}' as the current working directory",
base_directory.to_string_lossy()
)
})?;
}
let current_directory = Path::new(".");
if !filesystem::is_dir(current_directory) {
if !filesystem::is_existing_directory(current_directory) {
return Err(anyhow!(
"Could not retrieve current directory (has it been deleted?)."
));
@@ -84,7 +96,7 @@ fn run() -> Result<ExitCode> {
let mut directories = vec![];
for path in paths {
let path_buffer = PathBuf::from(path);
if filesystem::is_dir(&path_buffer) {
if filesystem::is_existing_directory(&path_buffer) {
directories.push(path_buffer);
} else {
print_error(format!(
@@ -109,7 +121,7 @@ fn run() -> Result<ExitCode> {
.iter()
.map(|path_buffer| {
path_buffer
.canonicalize()
.normalize()
.and_then(|pb| filesystem::absolute_path(pb.as_path()))
.unwrap()
})
@@ -119,7 +131,7 @@ fn run() -> Result<ExitCode> {
// Detect if the user accidentally supplied a path instead of a search pattern
if !matches.is_present("full-path")
&& pattern.contains(std::path::MAIN_SEPARATOR)
&& filesystem::is_dir(Path::new(pattern))
&& Path::new(pattern).is_dir()
{
return Err(anyhow!(
"The search pattern '{pattern}' contains a path-separation character ('{sep}') \
@@ -148,28 +160,50 @@ fn run() -> Result<ExitCode> {
let case_sensitive = !matches.is_present("ignore-case")
&& (matches.is_present("case-sensitive") || pattern_has_uppercase_char(&pattern_regex));
#[cfg(windows)]
let ansi_colors_support =
ansi_term::enable_ansi_support().is_ok() || std::env::var_os("TERM").is_some();
#[cfg(not(windows))]
let ansi_colors_support = true;
let interactive_terminal = atty::is(Stream::Stdout);
let colored_output = match matches.value_of("color") {
Some("always") => true,
Some("never") => false,
_ => env::var_os("NO_COLOR").is_none() && interactive_terminal,
_ => ansi_colors_support && env::var_os("NO_COLOR").is_none() && interactive_terminal,
};
let path_separator = matches.value_of("path-separator").map(|str| str.to_owned());
let path_separator = matches
.value_of("path-separator")
.map_or_else(filesystem::default_path_separator, |s| Some(s.to_owned()));
#[cfg(windows)]
let colored_output = colored_output && ansi_term::enable_ansi_support().is_ok();
{
if let Some(ref sep) = path_separator {
if sep.len() > 1 {
return Err(anyhow!(
"A path separator must be exactly one byte, but \
the given separator is {} bytes: '{}'.\n\
In some shells on Windows, '/' is automatically \
expanded. Try to use '//' instead.",
sep.len(),
sep
));
};
};
}
let ls_colors = if colored_output {
Some(LsColors::from_env().unwrap_or_default())
Some(LsColors::from_env().unwrap_or_else(|| LsColors::from_string(DEFAULT_LS_COLORS)))
} else {
None
};
let command = if let Some(args) = matches.values_of("exec") {
Some(CommandTemplate::new(args))
Some(CommandTemplate::new(args, path_separator.clone()))
} else if let Some(args) = matches.values_of("exec-batch") {
Some(CommandTemplate::new_batch(args)?)
Some(CommandTemplate::new_batch(args, path_separator.clone())?)
} else if matches.is_present("list-details") {
let color = matches.value_of("color").unwrap_or("auto");
let color_arg = ["--color=", color].concat();
@@ -249,7 +283,7 @@ fn run() -> Result<ExitCode> {
));
};
Some(CommandTemplate::new_batch(&cmd).unwrap())
Some(CommandTemplate::new_batch(&cmd, path_separator.clone()).unwrap())
} else {
None
};
@@ -314,21 +348,38 @@ fn run() -> Result<ExitCode> {
.value_of("max-depth")
.or_else(|| matches.value_of("rg-depth"))
.or_else(|| matches.value_of("exact-depth"))
.and_then(|n| usize::from_str_radix(n, 10).ok()),
.map(|n| n.parse::<usize>())
.transpose()
.context("Failed to parse argument to --max-depth/--exact-depth")?,
min_depth: matches
.value_of("min-depth")
.or_else(|| matches.value_of("exact-depth"))
.and_then(|n| usize::from_str_radix(n, 10).ok()),
.map(|n| n.parse::<usize>())
.transpose()
.context("Failed to parse argument to --min-depth/--exact-depth")?,
prune: matches.is_present("prune"),
threads: std::cmp::max(
matches
.value_of("threads")
.and_then(|n| usize::from_str_radix(n, 10).ok())
.map(|n| n.parse::<usize>())
.transpose()
.context("Failed to parse number of threads")?
.map(|n| {
if n > 0 {
Ok(n)
} else {
Err(anyhow!("Number of threads must be positive."))
}
})
.transpose()?
.unwrap_or_else(num_cpus::get),
1,
),
max_buffer_time: matches
.value_of("max-buffer-time")
.and_then(|n| u64::from_str_radix(n, 10).ok())
.map(|n| n.parse::<u64>())
.transpose()
.context("Failed to parse max. buffer time argument")?
.map(time::Duration::from_millis),
ls_colors,
interactive_terminal,
@@ -373,11 +424,11 @@ fn run() -> Result<ExitCode> {
exclude_patterns: matches
.values_of("exclude")
.map(|v| v.map(|p| String::from("!") + p).collect())
.unwrap_or_else(|| vec![]),
.unwrap_or_else(Vec::new),
ignore_files: matches
.values_of("ignore-file")
.map(|vs| vs.map(PathBuf::from).collect())
.unwrap_or_else(|| vec![]),
.unwrap_or_else(Vec::new),
size_constraints: size_limits,
time_constraints,
#[cfg(unix)]
@@ -386,8 +437,10 @@ fn run() -> Result<ExitCode> {
path_separator,
max_results: matches
.value_of("max-results")
.and_then(|n| usize::from_str_radix(n, 10).ok())
.filter(|&n| n != 0)
.map(|n| n.parse::<usize>())
.transpose()
.context("Failed to parse --max-results argument")?
.filter(|&n| n > 0)
.or_else(|| {
if matches.is_present("max-one-result") {
Some(1)
@@ -397,6 +450,17 @@ fn run() -> Result<ExitCode> {
}),
};
if cfg!(unix)
&& config.ignore_hidden
&& pattern_matches_strings_with_leading_dot(&pattern_regex)
{
return Err(anyhow!(
"The pattern seems to only match files with a leading dot, but hidden files are \
filtered by default. Consider adding -H/--hidden to search hidden files as well \
or adjust your search pattern."
));
}
let re = RegexBuilder::new(&pattern_regex)
.case_insensitive(!config.case_sensitive)
.dot_matches_new_line(true)
@@ -420,7 +484,7 @@ fn main() {
process::exit(exit_code.into());
}
Err(err) => {
eprintln!("[fd error]: {}", err);
eprintln!("[fd error]: {:#}", err);
process::exit(ExitCode::GeneralError.into());
}
}
+3
View File
@@ -48,6 +48,9 @@ pub struct Options {
/// The minimum depth for reported entries, or `None`.
pub min_depth: Option<usize>,
/// Whether to stop traversing into matching directories.
pub prune: bool,
/// The number of threads to use.
pub threads: usize,
+17 -12
View File
@@ -1,42 +1,47 @@
use std::io::{self, StdoutLock, Write};
use std::path::{Path, PathBuf};
use std::path::Path;
use std::process;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use ansi_term;
use lscolors::{LsColors, Style};
use crate::error::print_error;
use crate::exit_codes::ExitCode;
use crate::filesystem::strip_current_dir;
use crate::options::Options;
pub fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
path.replace(std::path::MAIN_SEPARATOR, &new_path_separator)
fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
path.replace(std::path::MAIN_SEPARATOR, new_path_separator)
}
// TODO: this function is performance critical and can probably be optimized
pub fn print_entry(
stdout: &mut StdoutLock,
entry: &PathBuf,
entry: &Path,
config: &Options,
wants_to_quit: &Arc<AtomicBool>,
) {
let path = if entry.is_absolute() {
entry.as_path()
entry
} else {
strip_current_dir(entry)
};
let r = if let Some(ref ls_colors) = config.ls_colors {
print_entry_colorized(stdout, path, config, ls_colors, &wants_to_quit)
print_entry_colorized(stdout, path, config, ls_colors, wants_to_quit)
} else {
print_entry_uncolorized(stdout, path, config)
};
if r.is_err() {
// Probably a broken pipe. Exit gracefully.
process::exit(ExitCode::GeneralError.into());
if let Err(e) = r {
if e.kind() == ::std::io::ErrorKind::BrokenPipe {
// Exit gracefully in case of a broken pipe (e.g. 'fd ... | head -n 3').
process::exit(0);
} else {
print_error(format!("Could not write to output: {}", e));
process::exit(ExitCode::GeneralError.into());
}
}
}
@@ -58,7 +63,7 @@ fn print_entry_colorized(
let mut path_string = component.to_string_lossy();
if let Some(ref separator) = config.path_separator {
*path_string.to_mut() = replace_path_separator(&path_string, &separator);
*path_string.to_mut() = replace_path_separator(&path_string, separator);
}
write!(stdout, "{}", style.paint(path_string))?;
@@ -86,7 +91,7 @@ fn print_entry_uncolorized_base(
let mut path_string = path.to_string_lossy();
if let Some(ref separator) = config.path_separator {
*path_string.to_mut() = replace_path_separator(&path_string, &separator);
*path_string.to_mut() = replace_path_separator(&path_string, separator);
}
write!(stdout, "{}{}", path_string, separator)
}
+48
View File
@@ -34,6 +34,45 @@ fn hir_has_uppercase_char(hir: &Hir) -> bool {
}
}
/// Determine if a regex pattern only matches strings starting with a literal dot (hidden files)
pub fn pattern_matches_strings_with_leading_dot(pattern: &str) -> bool {
let mut parser = ParserBuilder::new().allow_invalid_utf8(true).build();
parser
.parse(pattern)
.map(|hir| hir_matches_strings_with_leading_dot(&hir))
.unwrap_or(false)
}
/// See above.
fn hir_matches_strings_with_leading_dot(hir: &Hir) -> bool {
use regex_syntax::hir::*;
// Note: this only really detects the simplest case where a regex starts with
// "^\\.", i.e. a start text anchor and a literal dot character. There are a lot
// of other patterns that ONLY match hidden files, e.g. ^(\\.foo|\\.bar) which are
// not (yet) detected by this algorithm.
match *hir.kind() {
HirKind::Concat(ref hirs) => {
let mut hirs = hirs.iter();
if let Some(hir) = hirs.next() {
if *hir.kind() != HirKind::Anchor(Anchor::StartText) {
return false;
}
} else {
return false;
}
if let Some(hir) = hirs.next() {
*hir.kind() == HirKind::Literal(Literal::Unicode('.'))
} else {
false
}
}
_ => false,
}
}
#[test]
fn pattern_has_uppercase_char_simple() {
assert!(pattern_has_uppercase_char("A"));
@@ -50,3 +89,12 @@ fn pattern_has_uppercase_char_advanced() {
assert!(!pattern_has_uppercase_char(r"\Acargo"));
assert!(!pattern_has_uppercase_char(r"carg\x6F"));
}
#[test]
fn matches_strings_with_leading_dot_simple() {
assert!(pattern_matches_strings_with_leading_dot("^\\.gitignore"));
assert!(!pattern_matches_strings_with_leading_dot("^.gitignore"));
assert!(!pattern_matches_strings_with_leading_dot("\\.gitignore"));
assert!(!pattern_matches_strings_with_leading_dot("^gitignore"));
}
+43 -22
View File
@@ -87,10 +87,10 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Options>) ->
let config_dir_op = std::env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from)
.filter(|p| p.is_absolute())
.or_else(|| dirs::home_dir().map(|d| d.join(".config")));
.or_else(|| dirs_next::home_dir().map(|d| d.join(".config")));
#[cfg(not(target_os = "macos"))]
let config_dir_op = dirs::config_dir();
let config_dir_op = dirs_next::config_dir();
if let Some(global_ignore_file) = config_dir_op
.map(|p| p.join("fd").join("ignore"))
@@ -170,12 +170,13 @@ fn spawn_receiver(
let show_filesystem_errors = config.show_filesystem_errors;
let threads = config.threads;
// This will be used to check if output should be buffered when only running a single thread
let enable_output_buffering: bool = threads > 1;
thread::spawn(move || {
// This will be set to `Some` if the `--exec` argument was supplied.
if let Some(ref cmd) = config.command {
if cmd.in_batch_mode() {
exec::batch(rx, cmd, show_filesystem_errors)
exec::batch(rx, cmd, show_filesystem_errors, enable_output_buffering)
} else {
let shared_rx = Arc::new(Mutex::new(rx));
@@ -189,8 +190,15 @@ fn spawn_receiver(
let out_perm = Arc::clone(&out_perm);
// Spawn a job thread that will listen for and execute inputs.
let handle =
thread::spawn(move || exec::job(rx, cmd, out_perm, show_filesystem_errors));
let handle = thread::spawn(move || {
exec::job(
rx,
cmd,
out_perm,
show_filesystem_errors,
enable_output_buffering,
)
});
// Push the handle of the spawned thread into the vector for later joining.
handles.push(handle);
@@ -358,19 +366,27 @@ fn spawn_senders(
DirEntry::BrokenSymlink(path)
}
_ => {
tx_thread
.send(WorkerResult::Error(ignore::Error::WithPath {
path,
err: inner_err,
}))
.unwrap();
return ignore::WalkState::Continue;
match tx_thread.send(WorkerResult::Error(ignore::Error::WithPath {
path,
err: inner_err,
})) {
Ok(_) => {
return ignore::WalkState::Continue;
}
Err(_) => {
return ignore::WalkState::Quit;
}
}
}
},
Err(err) => match tx_thread.send(WorkerResult::Error(err)) {
Ok(_) => {
return ignore::WalkState::Continue;
}
Err(_) => {
return ignore::WalkState::Quit;
}
},
Err(err) => {
tx_thread.send(WorkerResult::Error(err)).unwrap();
return ignore::WalkState::Continue;
}
};
if let Some(min_depth) = config.min_depth {
@@ -418,8 +434,8 @@ fn spawn_senders(
if (!file_types.files && entry_type.is_file())
|| (!file_types.directories && entry_type.is_dir())
|| (!file_types.symlinks && entry_type.is_symlink())
|| (!file_types.sockets && filesystem::is_socket(entry_type))
|| (!file_types.pipes && filesystem::is_pipe(entry_type))
|| (!file_types.sockets && filesystem::is_socket(*entry_type))
|| (!file_types.pipes && filesystem::is_pipe(*entry_type))
|| (file_types.executables_only
&& !entry
.metadata()
@@ -429,8 +445,8 @@ fn spawn_senders(
|| !(entry_type.is_file()
|| entry_type.is_dir()
|| entry_type.is_symlink()
|| filesystem::is_socket(entry_type)
|| filesystem::is_pipe(entry_type))
|| filesystem::is_socket(*entry_type)
|| filesystem::is_pipe(*entry_type))
{
return ignore::WalkState::Continue;
}
@@ -443,7 +459,7 @@ fn spawn_senders(
{
if let Some(ref owner_constraint) = config.owner_constraint {
if let Ok(ref metadata) = entry_path.metadata() {
if !owner_constraint.matches(&metadata) {
if !owner_constraint.matches(metadata) {
return ignore::WalkState::Continue;
}
} else {
@@ -494,6 +510,11 @@ fn spawn_senders(
return ignore::WalkState::Quit;
}
// Apply pruning.
if config.prune {
return ignore::WalkState::Skip;
}
ignore::WalkState::Continue
})
});
+30 -17
View File
@@ -119,7 +119,7 @@ fn normalize_output(s: &str, trim_start: bool, normalize_line: bool) -> String {
let line = line.replace('/', &std::path::MAIN_SEPARATOR.to_string());
if normalize_line {
let mut words: Vec<_> = line.split_whitespace().collect();
words.sort();
words.sort_unstable();
return words.join(" ");
}
line
@@ -197,7 +197,7 @@ impl TestEnv {
// Check for exit status.
if !output.status.success() {
panic!(format_exit_error(args, &output));
panic!("{}", format_exit_error(args, &output));
}
output
@@ -236,22 +236,30 @@ impl TestEnv {
// Compare actual output to expected output.
if expected != actual {
panic!(format_output_error(args, &expected, &actual));
panic!("{}", format_output_error(args, &expected, &actual));
}
}
/// Assert that calling *fd* with the specified arguments produces the expected error,
/// and does not succeed.
pub fn assert_failure_with_error(&self, args: &[&str], expected: &str) {
let status = self.assert_error_subdirectory(".", args, expected);
let status = self.assert_error_subdirectory(".", args, Some(expected));
if status.success() {
panic!("error '{}' did not occur.", expected);
}
}
/// Assert that calling *fd* with the specified arguments does not succeed.
pub fn assert_failure(&self, args: &[&str]) {
let status = self.assert_error_subdirectory(".", args, None);
if status.success() {
panic!("Failure did not occur as expected.");
}
}
/// Assert that calling *fd* with the specified arguments produces the expected error.
pub fn assert_error(&self, args: &[&str], expected: &str) -> process::ExitStatus {
self.assert_error_subdirectory(".", args, expected)
self.assert_error_subdirectory(".", args, Some(expected))
}
/// Assert that calling *fd* in the specified path under the root working directory,
@@ -260,7 +268,7 @@ impl TestEnv {
&self,
path: P,
args: &[&str],
expected: &str,
expected: Option<&str>,
) -> process::ExitStatus {
// Setup *fd* command.
let mut cmd = process::Command::new(&self.fd_exe);
@@ -270,19 +278,24 @@ impl TestEnv {
// Run *fd*.
let output = cmd.output().expect("fd output");
// Normalize both expected and actual output.
let expected_error = normalize_output(expected, true, self.normalize_line);
let actual_err = normalize_output(
&String::from_utf8_lossy(&output.stderr),
false,
self.normalize_line,
);
if let Some(expected) = expected {
// Normalize both expected and actual output.
let expected_error = normalize_output(expected, true, self.normalize_line);
let actual_err = normalize_output(
&String::from_utf8_lossy(&output.stderr),
false,
self.normalize_line,
);
// Compare actual output to expected output.
if !actual_err.trim_start().starts_with(&expected_error) {
panic!(format_output_error(args, &expected_error, &actual_err));
// Compare actual output to expected output.
if !actual_err.trim_start().starts_with(&expected_error) {
panic!(
"{}",
format_output_error(args, &expected_error, &actual_err)
);
}
}
return output.status;
output.status
}
}
+203 -12
View File
@@ -5,6 +5,7 @@ use std::io::Write;
use std::path::Path;
use std::time::{Duration, SystemTime};
use normpath::PathExt;
use regex::escape;
use crate::testenv::TestEnv;
@@ -23,11 +24,13 @@ static DEFAULT_FILES: &[&str] = &[
"e1 e2",
];
#[allow(clippy::let_and_return)]
fn get_absolute_root_path(env: &TestEnv) -> String {
let path = env
.test_root()
.canonicalize()
.normalize()
.expect("absolute path")
.as_path()
.to_str()
.expect("string")
.to_string();
@@ -758,6 +761,40 @@ fn test_exact_depth() {
);
}
/// Pruning (--prune)
#[test]
fn test_prune() {
let dirs = &["foo/bar", "bar/foo", "baz"];
let files = &[
"foo/foo.file",
"foo/bar/foo.file",
"bar/foo.file",
"bar/foo/foo.file",
"baz/foo.file",
];
let te = TestEnv::new(dirs, files);
te.assert_output(
&["foo"],
"foo
foo/foo.file
foo/bar/foo.file
bar/foo.file
bar/foo
bar/foo/foo.file
baz/foo.file",
);
te.assert_output(
&["--prune", "foo"],
"foo
bar/foo
bar/foo.file
baz/foo.file",
);
}
/// Absolute paths (--absolute-path)
#[test]
fn test_absolute_path() {
@@ -982,6 +1019,34 @@ fn test_extension() {
te4.assert_output(&["--hidden", "--extension", ".hidden"], "test.hidden");
}
/// No file extension (test for the pattern provided in the --help text)
#[test]
fn test_no_extension() {
let te = TestEnv::new(
DEFAULT_DIRS,
&["a.foo", "aa", "one/b.foo", "one/bb", "one/two/three/d"],
);
te.assert_output(
&["^[^.]+$"],
"aa
one
one/bb
one/two
one/two/three
one/two/three/d
one/two/three/directory_foo
symlink",
);
te.assert_output(
&["^[^.]+$", "--type", "file"],
"aa
one/bb
one/two/three/d",
);
}
/// Symlink as search directory
#[test]
fn test_symlink_as_root() {
@@ -1028,16 +1093,19 @@ fn test_symlink_as_root() {
fn test_symlink_and_absolute_path() {
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
let expected_path = if cfg!(windows) { "symlink" } else { "one/two" };
te.assert_output_subdirectory(
"symlink",
&["--absolute-path"],
&format!(
"{abs_path}/one/two/c.foo
{abs_path}/one/two/C.Foo2
{abs_path}/one/two/three
{abs_path}/one/two/three/d.foo
{abs_path}/one/two/three/directory_foo",
abs_path = &abs_path
"{abs_path}/{expected_path}/c.foo
{abs_path}/{expected_path}/C.Foo2
{abs_path}/{expected_path}/three
{abs_path}/{expected_path}/three/d.foo
{abs_path}/{expected_path}/three/directory_foo",
abs_path = &abs_path,
expected_path = expected_path
),
);
}
@@ -1065,6 +1133,8 @@ fn test_symlink_and_full_path() {
let root = te.system_root();
let prefix = escape(&root.to_string_lossy());
let expected_path = if cfg!(windows) { "symlink" } else { "one/two" };
te.assert_output_subdirectory(
"symlink",
&[
@@ -1073,10 +1143,11 @@ fn test_symlink_and_full_path() {
&format!("^{prefix}.*three", prefix = prefix),
],
&format!(
"{abs_path}/one/two/three
{abs_path}/one/two/three/d.foo
{abs_path}/one/two/three/directory_foo",
abs_path = &abs_path
"{abs_path}/{expected_path}/three
{abs_path}/{expected_path}/three/d.foo
{abs_path}/{expected_path}/three/directory_foo",
abs_path = &abs_path,
expected_path = expected_path
),
);
}
@@ -1275,6 +1346,85 @@ fn test_exec_batch() {
}
}
/// Shell script execution (--exec) with a custom --path-separator
#[test]
fn test_exec_with_separator() {
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
te.assert_output(
&[
"--path-separator=#",
"--absolute-path",
"foo",
"--exec",
"echo",
],
&format!(
"{abs_path}#a.foo
{abs_path}#one#b.foo
{abs_path}#one#two#C.Foo2
{abs_path}#one#two#c.foo
{abs_path}#one#two#three#d.foo
{abs_path}#one#two#three#directory_foo",
abs_path = abs_path.replace(std::path::MAIN_SEPARATOR, "#"),
),
);
te.assert_output(
&["--path-separator=#", "foo", "--exec", "echo", "{}"],
"a.foo
one#b.foo
one#two#C.Foo2
one#two#c.foo
one#two#three#d.foo
one#two#three#directory_foo",
);
te.assert_output(
&["--path-separator=#", "foo", "--exec", "echo", "{.}"],
"a
one#b
one#two#C
one#two#c
one#two#three#d
one#two#three#directory_foo",
);
te.assert_output(
&["--path-separator=#", "foo", "--exec", "echo", "{/}"],
"a.foo
b.foo
C.Foo2
c.foo
d.foo
directory_foo",
);
te.assert_output(
&["--path-separator=#", "foo", "--exec", "echo", "{/.}"],
"a
b
C
c
d
directory_foo",
);
te.assert_output(
&["--path-separator=#", "foo", "--exec", "echo", "{//}"],
".
one
one#two
one#two
one#two#three
one#two#three",
);
te.assert_output(
&["--path-separator=#", "e1", "--exec", "printf", "%s.%s\n"],
"e1 e2.",
);
}
/// Literal search (--fixed-strings)
#[test]
fn test_fixed_strings() {
@@ -1353,6 +1503,9 @@ fn test_size() {
// Zero sized files.
te.assert_output(&["", "--size", "-0B"], "0_bytes.foo");
te.assert_output(&["", "--size", "0B"], "0_bytes.foo");
te.assert_output(&["", "--size=0B"], "0_bytes.foo");
te.assert_output(&["", "-S", "0B"], "0_bytes.foo");
// Files with 2 bytes or more.
te.assert_output(
@@ -1369,6 +1522,9 @@ fn test_size() {
// Files with size between 1 byte and 11 bytes.
te.assert_output(&["", "--size", "+1B", "--size", "-11B"], "11_bytes.foo");
// Files with size equal 11 bytes.
te.assert_output(&["", "--size", "11B"], "11_bytes.foo");
// Files with size between 1 byte and 30 bytes.
te.assert_output(
&["", "--size", "+1B", "--size", "-30B"],
@@ -1400,6 +1556,7 @@ fn test_size() {
// Files with size equal 4 kibibytes.
te.assert_output(&["", "--size", "+4ki", "--size", "-4ki"], "4_kibibytes.foo");
te.assert_output(&["", "--size", "4ki"], "4_kibibytes.foo");
}
#[cfg(test)]
@@ -1515,7 +1672,7 @@ fn test_base_directory() {
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
let abs_base_dir = &format!("{abs_path}/one/two", abs_path = &abs_path);
te.assert_output(
&["--base-directory", &abs_base_dir, "foo", &abs_path],
&["--base-directory", abs_base_dir, "foo", &abs_path],
&format!(
"{abs_path}/a.foo
{abs_path}/one/b.foo
@@ -1611,3 +1768,37 @@ fn test_list_details() {
// Make sure we can execute 'fd --list-details' without any errors.
te.assert_success_and_get_output(".", &["--list-details"]);
}
/// Make sure that fd fails if numeric arguments can not be parsed
#[test]
fn test_number_parsing_errors() {
let te = TestEnv::new(&[], &[]);
te.assert_failure(&["--threads=a"]);
te.assert_failure(&["-j", ""]);
te.assert_failure(&["--threads=0"]);
te.assert_failure(&["--min-depth=a"]);
te.assert_failure(&["--max-depth=a"]);
te.assert_failure(&["--maxdepth=a"]);
te.assert_failure(&["--exact-depth=a"]);
te.assert_failure(&["--max-buffer-time=a"]);
te.assert_failure(&["--max-results=a"]);
}
/// Print error if search pattern starts with a dot and --hidden is not set
/// (Unix only, hidden files on Windows work differently)
#[test]
#[cfg(unix)]
fn test_error_if_hidden_not_set_and_pattern_starts_with_dot() {
let te = TestEnv::new(&[], &[".gitignore", ".whatever", "non-hidden"]);
te.assert_failure(&["^\\.gitignore"]);
te.assert_failure(&["--glob", ".gitignore"]);
te.assert_output(&["--hidden", "^\\.gitignore"], ".gitignore");
te.assert_output(&["--hidden", "--glob", ".gitignore"], ".gitignore");
te.assert_output(&[".gitignore"], "");
}