Finder/Terminal: Find files that contain less than 21 lines of text
Solution 1:
In Terminal you can combine find
and wc
for this:
find /path/to/directory -type f \
-exec bash -c '[[ $(wc -l < "$1") -lt 21 ]]' _ {} \; -print
This will find all files (-type f
) beneath /path/to/directory
, count the lines (wc -l < "{}"
, {}
gets replaced by any file found) and print the file name for files containing less than 21 lines.
PS: It will also try to count the lines in non-text files. If this causes issues, use the following instead:
find /path/to/directory -type f -exec bash -c 'file -b "$1" | grep -q text' _ {} \; \
-exec bash -c '[[ $(wc -l < "$1") -lt 21 ]]' _ {} \; -print
PPS: To start from the current directory, replace the path at the beginning with .
(a single dot, for the current directory)
PPPS: To restrict the search to the current directory, use find . -maxdepth 1 -type f ...
Solution 2:
Here's another shell solution. We start from the current working directory .
-
find . -type f -exec wc -l {} + | sed '$d'| awk '$1 < 21 { $1=""; print}'
find
filters just files and executes wc
for all the files found. The output is piped to sed
where we remove the last line which would be something like- ### Total
.
awk
then processes each line of output for the first column to be less than 21 lines and prints the filename in column 2 of the output on wards.
Solution 3:
I'm not going to "code golf" this (shortest wins).. This is more like "code basketball".
Because your needs are a bit complex, I say use the most tunable option.
We launch with
Find (root directory) -type f | perl count21
Count21 is a file that contains some perl.
while (<>) { # read each line of input into variable $_
chomp $_; # Removing newline at end of line, if exists. It does.
local $/ = undef; # Input will ignore newlines and slurp in entire file
open (my $IN, "<", $_); # immune to < > ' " tab etc. in filename
my $text = <$IN>; # read entire file
chomp $text; # remove last newline if exists, so files with or
# without trailing newlines are the same.
$lc = ( $text =~ tr/\n// ) + 1; # tr/\n// counts newline characters.
# $text =~ says apply this operation to $text.
# The value of this operation is char count.
# Add 1 so we count the last line.
# This count will be wrong on 0-line files,
# but that's outside of problem scope.
if (21 >= $lc) { # if $lc <= 21 (Putting var first is bad luck)
print $_, "\n"; # print filename, and a newline
} # endif
} # end while(<>)
Bothering with all this perl and avoiding every shortcut seems super stupid. The reason to do it is you can far more easily cope with unexpected wrinkles, like the "some lines don't end in newline" problem, easily fixed with a chomp and a +1 here.
This also nukes-from-orbit the problem you'd have with passing file lists in shell, such as filenames with spaces, ", ', |, tab and other monkeywrenches. Unfortunately piping from Find won't handle newline in a filename, but the Perl File::Find module will.
It also makes it easy to shim in even more stuff: for instance if you only want files with "diddle", add
next if not ( $text =~ /diddle/i );