In Powershell, how do I list all files in a directory (recursively) that contain text that matches a given regex? The files in question contain really long lines of incomprehensible text, so I don't want to see the matching line — just the filename.
PowerShell – Equivalent of `grep -r -l` (–files-with-matches)
greppowershell
Related Solutions
To count the matches, listing only the filename(s) and count:
grep -src HOST /etc/*
Example output:
/etc/postfix/postfix-files:1
/etc/security/pam_env.conf:6
/etc/X11/app-defaults/Ddd.3.3.11:1
/etc/X11/app-defaults/Ddd:1
/etc/zsh/zshrc:0
/etc/zsh/zshenv:0
The -c
option supresses normal output and prints a match count for each file.
If you'd like to suppress the files with zero counts:
grep -src HOST /etc/* | grep -v ':0$'
To print the line number (-n
) and file name (-H
) for each matching line for any number of input files:
grep -srnH HOST /etc/*
Example output:
/etc/lynx-cur/lynx.cfg:254:.h2 LYNX_HOST_NAME
/etc/lynx-cur/lynx.cfg:255:# If LYNX_HOST_NAME is defined here or in userdefs.h, it will be
/etc/X11/app-defaults/Ddd.3.3.11:8005: DDD 3.3.11 (@THEHOST@) gets @CAUSE@\n\
/etc/X11/app-defaults/Ddd:8010: DDD 3.3.12 (@THEHOST@) gets @CAUSE@\n\
The option -r
causes grep
to recursively search files in each subdirectory at all levels under the specified directory. The -s
option suppresses error messages.
To suppress matches of binary files, use the -I
option.
See man grep
for more information.
Here is one script that works and is reasonably efficient. Note that it does require precisely one space to have been added before the "(1)" and none to have been added after for it to work.
#!/usr/bin/bash
IFS=$'\n';
set -f
#Go deepest first to deal with copies within copied folders.
for copy in $(find . -regextype posix-egrep -regex "^.*\ \([0-9]+\)\s*(\.[^/.]*)?$" | awk '{print length($0)"\t"$0}' | sort -rnk1 | cut -f2-); do
orig=$(rev <<< "$copy" | sed -E 's/\)[0-9]+\(\ //' | rev)
if [ "$orig" != "$copy" ]; then
if [ -f "$orig" ]; then
if [ -f "$copy" ]; then
echo "File pair: $orig $copy"
if diff -q "$orig" "$copy" &>/dev/null; then
echo "Removing file: $copy"
rm -f "$copy";
fi
fi
fi
if [ -d "$orig" ]; then
if [ -d "$copy" ]; then
echo "Folder pair: $orig $copy"
if rmdir "$copy" &>/dev/null; then
#If the "copy" was an empty directory then we've removed it and so we're done.
echo "Removed empty folder: $copy"
else
#Non-destructively ensure that both folders have the same files at least.
rsync -aHAv --ignore-existing "$orig/" "$copy" &>/dev/null
rsync -aHAv --ignore-existing "$copy/" "$orig" &>/dev/null
if diff -qr "$orig" "$copy" &>/dev/null; then
echo "Removing folder: $copy"
rm -rf "$copy";
fi
fi
fi
fi
fi
done
unset IFS;
set +f
Best Answer
You can use
Select-String
to search for text inside files, andSelect-Object
to return specific properties for each match. Something like this:Or a shorter version, using aliases:
If you want just the filenames, not full paths, replace
Path
withFilename
.Explanation:
Get-ChildItem
-Recurse *.*
returns all files in the current directory and all its subdirectories.Select-String
-Pattern "foobar"
searches those files for the given pattern "foobar".Select-Object
-Unique Path
returns only the file path for each match; the-Unique
parameter eliminates duplicates.