Exclude directory when grepping with zsh globbing - linux

I have some file structure which contains projects with build folders at various depths. I'm trying to use zsh (extended) globbing to exclude files in build folders.
I tried using the following command and many other variants:
grep "string" (^build/)#
I'm reading this as "Any folder that doesn't match build 0 or more times."
However I'm still getting results from folders such as:
./ProjectA/build/.../file.mi
./ProjectB/package/build/.../file2.mi
Any suggestions?

This should work:
grep string (^build/)#*(.)
Explanation:
^build: anything not named build
^build/: any directory not named build. It will not match any other file type
(^build/)#: any directory path consisting out of elements that are not named build. Again, this will not match a path where the last element is not a directory
(^build/)#*: Any path where all but the last element must not be named build. This will also list files. It also assumes that it would be ok, if the file itself were named build. If that is not the case you have to use (^build/)#^build
(^build/)#*(.): Same as before, but restricted to only match normal files by the glob qualifier (.)

I think you don't need to involve the shell for that task; grep comes with its own file-globber. From manpage:
--exclude-dir=GLOB
Skip any command-line directory with a name suffix that matches the pattern GLOB. When searching recursively, skip any subdirectory whose
base name matches GLOB. Ignore any redundant trailing slashes in GLOB.
So something like this should get the job done for you:
grep -R "string" --exclude-dir='build'
That filter will leave out subdirectories called exactly "build"; if you want to filter out directories that contain the string "build" (such as "build2" or "test-build") then use globbing inside the exclusion pattern:
grep -R "string" --exclude-dir='*build*'
For completeness' sake, I also include here how to do the same thing with two popular grep alternatives that I'm more or less familiar with:
The Silver Searcher: ag -G '^((?!build).)*$' string
Ripgrep: rg -g '!build'

Using the glob qualifier e did the trick for me, both for 'grep' and 'ls':
grep -s "string" **/*(e[' [[ ! `echo "$REPLY" | grep "build/" ` ]]'])

Related

How does the ** work while searching for a Path

I didn't find a lot of info about this, as far as I know it matches filenames and directories recursively, but how does it work?
The glob-expression ** is used to match all files and zero or more directories and subdirectories. If the pattern is followed by a /, only directories and subdirectories match.
This means that it is used in a recursive file-search during path-name expansion patterns on the command line.
Depending on the shell you use, it needs to be enabled. In bash this is done with:
$ shopt -s globstar
Here are examples:
# list all files recursively
$ echo **
# list all files recursively that end with .txt
$ echo **/*.txt
# list all files recursively that are in a subdirectory foo
$ echo **/foo/**
Beware that the following pattern does not work recursively **.txt. This is just seen as a combination of two single asterisk globs and is identical to *.txt.
Note: there are subtle differences between bash and zsh, but in general it works the same.

Copying files with even number in its name - bash

I want to copy all files from /usr/lib which ends with .X.0.0 where X is an even number. Is there a better way than the following one to select all the files?
ls /usr/lib | grep "[02468].0.0$"
My problem with this solutions is that in case there are files with names like "xy.800.0.0" I need to use the bracket three times etc.
Just use a glob expansion to match the files:
cp /usr/lib/*.*[02468].0.0 /path/to/destination
The shell expands this pattern to the list of files before passing them as arguments to cp.
Since you tagged Bash, you can make the match more strict by using an extended glob:
shopt -s extglob failglob
cp /usr/lib/*.*([0-9])[02468].0.0 /path/to/destination
This matches 0 or more other digits followed by an even digit, and doesn't run the command at all if no files match.
You could use extended grep regular expressions to only match even numbers:
ls -1q /usr/lib | grep -E "\.[0-9]*[02468].0.0$"
However, as Tom suggested, there are better options than parsing the output of ls. It's generally safer and faster to use glob expansion, and more maintainable to just put everything in a python script.

Finding multiple strings in directory using linux commends

If I have two strings, for example "class" and "btn", what is the linux command that would allow me to search for these two strings in the entire directory.
To be more specific, lets say I have directory that contains few folders with bunch of .php files. My goal is to be able to search throughout those .php files so that it prints out only files that contain "class" and "btn" in one line. Hopefully this clarifies things better.
Thanks,
I normally use the following to search for strings inside my source codes. It searches for string and shows the exact line number where that text appears. Very helpful for searching string in source code files. You can always pipes the output to another grep and filter outputs.
grep -rn "text_to_search" directory_name/
example:
$ grep -rn "angular" menuapp
$ grep -rn "angular" menuapp | grep some_other_string
output would be:
menuapp/public/javascripts/angular.min.js:251://# sourceMappingURL=angular.min.js.map
menuapp/public/javascripts/app.js:1:var app = angular.module("menuApp", []);
grep -r /path/to/directory 'class|btn'
grep is used to search a string in a file. With the -r flag, it searches recursively all files in a directory.
Or, alternatively using the find command to "identify" the files to be searched instead of using grep in recursive mode:
find /path/to/your/directory -type f -exec grep "text_to_search" {} \+;

Renaming files in subfolders using zmv

Let's say I want to rename all the files inside all the subfolders of a folder from foo.txt to bar.txt using zmv.
I've tried zmv '**/foo.txt' 'bar.txt' but this creates bar.txt in the root folder. How can I keep the files in their corresponding subfolder?
You need to reference the directory part in the target. You can do that by putting the wildcards in parentheses and using $1 to refer to the part matched by the parenthetical group. The ** wildcard is a little special and requires that the parentheses are around **/, no more, no less.
zmv '(**/)foo.txt' '${1}bar.txt'
You can use the -w flag to have each wildcard automatically made into a parenthetical group.
zmv -w '**/foo.txt' '${1}bar.txt'
Or you can use the -W flag and use wildcards in the replacement text — with this flag, the wildcards in the replacement text are turned into $1, $2, etc.
zmv -W '**/foo.txt' '**/bar.txt'
Alternatively, you can use $f to refer to the source path.
zmv '**/foo.txt' '$f:r.txt'

grep recursively for a specific file type on Linux

Can we search a term (eg. "onblur") recursively in some folders only in specific files (html files)?
grep -Rin "onblur" *.html
This returns nothing. But,
grep -Rin "onblur" .
returns "onblur" search result from all available files, like in text(".txt"), .mako, .jinja etc.
Consider checking this answer and that one.
Also this might help you: grep certain file types recursively | commandlinefu.com.
The command is:
grep -r --include="*.[ch]" pattern .
And in your case it is:
grep -r --include="*.html" "onblur" .
grep -r --include "*.html" onblur .
Got it from :
How do I grep recursively?
You might also like ag 'the silver searcher' -
ag --html onblur
it searches by regexp and is recursive in the current directory by default, and has predefined sets of extensions to search - in this case --html maps to .htm, .html, .shtml, .xhtml. Also ignores binary files, prints filenames, line numbers, and colorizes output by default.
Some options -
-Q --literal
Do not parse PATTERN as a regular expression. Try to match it literally.
-S --smart-case
Match case-sensitively if there are any uppercase letters in PATTERN,
case-insensitively otherwise. Enabled by default.
-t --all-text
Search all text files. This doesn't include hidden files.
--hidden
Search hidden files. This option obeys ignored files.
For the list of supported filetypes run ag --list-file-types.
The only thing it seems to lack is being able to specify a filetype with an extension, in which case you need to fall back on grep with --include.
To be able to grep only from .py files by typing grepy mystring I added the following line to my bashrc:
alias grepy='grep -r --include="*.py"'
Also note that grep accepts The following:
grep mystring *.html
for .html search in current folder
grep mystring */*.html
for recursive search (excluding any file in current dir!).
grep mystring .*/*/*.html
for recursive search (all files in current dir and all files in subdirs)
Have a look at this answer instead, to a similar question: grep, but only certain file extensions
This worked for me. In your case just type the following:
grep -inr "onblur" --include \*.html ./
consider that
grep: command
-r: recursively
-i: ignore-case
-n: each output line is preceded by its relative line number in the file
--include \*.html: escape with \ just in case you have a directory with asterisks in the filenames
./: start at current directory.

Resources