I need to match files only with one specific extension under all nested directories, including the PWD, with BASH using "globbing".
- I do not need to Match all files under all nested directories with shell globbing, but not in the PWD.
- I do not need to only grep recursively, but only in files with certain extensions (plural)
set -o globstar; ls **/*.*
is great for all files (not my question).ls **/*.php
does not match in the PWD.set -o globstar; **/*.php
returns duplicate files.
grep -r --include=\*.php "find me" ./
is specifically forgrep
, not globbing (consider this Question). It seemsgrep
has--include=GLOB
because this is not possible using globbing.
From this Answer (here), I believe there may not be a way to do this using globbing.
tl;dr
I need:
- A glob expression
- To match any command where simple globs can be used (
ls
,sed
,cp
,cat
,chown
,rm
, et cetera) - Mainly in BASH, but other shells would be interesting
- Both in the PWD and all subdirectories recursively
- For files with a specific extension
I'm using grep
& ls
only as examples, but I need a glob expression that applies to other commands also.
grep -r --include=GLOB
is not a glob expression for, say,cp
; it is a workaround specific togrep
and is not a solution.find
is not a glob, but it may be a workaround for non-grep
commands if there is no such glob expression. It would need|
orwhile do;
, et cetera.
Examples
Suppose I have these files, all containing "find me":
./file1.js
./file2.php
./inc/file3.js
./inc/file4.php
./inc.php/file5.js
./inc.php/file6.php
I need to match only/all .php one time:
./file2.php
./inc/file4.php
./inc.php/file6.php
Duplicates returned: shopt -s globstar; ... **/*.php
This changes the problem; it does not solve it.
Dup: ls
Before entering shopt -s globstar
as a single command...
ls **/*.php
returns:
inc/file4.php
inc.php/file5.js
inc.php/file6.php
- file2.php does not return.
After entering shopt -s globstar
as a single command...
ls **/*.php
returns:
file2.php
inc/file4.php
inc.php/file6.php
inc.php:
file5.js
file6.php
- inc.php/file6.php returns twice.
Dup: grep
Before entering shopt -s globstar
as a single command...
grep -R "find me" **/*.php
returns:
inc/file4.php: find me
inc.php/file6.php: find me
- file2.php does not return.
After entering shopt -s globstar
as a single command...
grep -R "find me" **/*.php
returns:
file2.php: find me
inc/file4.php: find me
inc.php/file5.js: find me
inc.php/file6.php: find me
inc.php/file6.php: find me
- inc.php/file6.php returns twice.
- After seeing the duplicate seen from the
ls
output, we know why.
- After seeing the duplicate seen from the
Current solution: faulty misuse of &&
logic
grep -r "find me" *.php && grep -r "find me" */*.php
ls -l *.php && ls -l */*.php
- Please no!
I fail here && so I never happen
Desired solution: single command via globbing
grep -r "find me" [GLOB]
ls -l [GLOB]
Insight from grep
grep
does have the --include
flag, which achieves the same result but using a flag specific to grep
. ls
does not have an --include
option. This leads me to believe that there is no such glob expression, which is why grep
has this flag.
CodePudding user response:
With bash, you can first do a shopt -s globstar
to enable recursive matching, and then the pattern **/*.php
will expand to all the files in the current directory tree that have a .php extension.
zsh and ksh93 also support this syntax. Other commands that take a glob pattern as an argument and do their own expansion of it (like your grep --include
) likely won't.
CodePudding user response:
With shell globing it's possible to only get directories by adding a /
at the end of the glob, but there's no way to exclusively get files (zsh
beeing an exception)
Illustration
With the given tree:
file.php
inc.php/include.php
**/*.php/
will expand to inc.php/
, and **/*.php
will expand to file.php inc.php inc.php/include.php
If you want to get file.php inc.php/include.php
, then you cannot use a glob, that's all.
(with zsh
, the glob for that would be **/*.php(.)
)
Standard solution
The POSIX way to recursively get the files that match a given glob and then apply a command to them is to use find -type f -name ... -exec ...
:
ls -l <all .php files>
would be:
find . -type f -name '*.php' -exec ls -l {}
grep "finde me" <all .php files>
would be:
find . -type f -name '*.php' -exec grep "finde me" {}
cp <all .php files> ~/destination/
would be:
find . -type f -name '*.php' -type f -exec sh -c 'cp "$@" ~/destination/' _ {}
CodePudding user response:
Suggesting different strategy:
Use explicit find
command to build bash
command(s) on the selected files using -printf
option.
Inspect the command for correctness and run.
1. preparing bash
commands on selected files
find . -type f -name "*.php" -printf "cp %p ~/destination/ \n"
2. inspect the output, correct command, correct filter, test
cp ./file2.php ~/destination/
cp ./inc/file4.php ~/destination/
cp ./inc.php/file5.php ~/destination/
3. execute prepared find
output
bash <<< $(find . -type f -name "*.php" -printf "cp %f ~/destination/ \n")