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 need to match files using commands other than grep search all directories with filename extension
- 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 **/*.phpdoes not match in the PWD.set -o globstar; **/*.phpreturns duplicate files.
grep -r --include=\*.php "find me" ./is specifically forgrep, not globbing (consider this Question). It seemsgrephas--include=GLOBbecause 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=GLOBis not a glob expression for, say,cp; it is a workaround specific togrepand is not a solution.findis not a glob, but it may be a workaround for non-grepcommands 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
lsoutput, 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 is possible to only get directories by adding a / at the end of the glob, but there's no way to exclusively get files (zsh being an exception)
Illustration:
With the given tree:
file.php
inc.php/include.php
lib/lib.php
Supposing that the shell supports the non-standard ** glob:
**/*.php/expands toinc.php/**/*.phpexpands tofile.php inc.php inc.php/include.php lib/lib.phpFor getting
file.php inc.php/include.php lib/lib.php, you cannot use a glob.
=> withzshit would be**/*.php(.)
Standard work-around (any shell, any OS)
The POSIX way to recursively get the files that match a given standard 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/' _ {}
remark: This one is a little more tricky because you need ~/destination/ to be after the file arguments, and find's syntax doesn't allow find -exec ... {} ~/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")
