Repository: sharkdp/fd
Pull request #696 (2020-12-02 15:06:06)

Add exact size support to the --size filter (#669)
This will allow using `--size=100b` or `--size 100b` filters to search for files of that exact size.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4eaf010..c66220a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 - Improved the usability of the time-based options, see #624 and #645 (@gorogoroumaru)
 - Add new `--prune` flag, see #535 (@reima)
+- Add support for exact sizes in `--size` filter, see #669 (pull #696)
 
 ## Bugfixes
 
diff --git a/doc/fd.1 b/doc/fd.1
index 41158e1..3579d90 100644
--- a/doc/fd.1
+++ b/doc/fd.1
@@ -57,7 +57,7 @@ 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.
@@ -179,6 +179,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'
diff --git a/src/app.rs b/src/app.rs
index df30f48..b5dee4a 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -390,7 +390,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    \
diff --git a/src/filter/size.rs b/src/filter/size.rs
index 08f6c68..09714e7 100644
--- a/src/filter/size.rs
+++ b/src/filter/size.rs
@@ -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,
         }
     }
 }
@@ -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",


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 9a375f3..616189e 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -1387,6 +1387,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(
@@ -1403,6 +1406,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"],
@@ -1434,6 +1440,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)]