Bash - alternative for: ls | grep - linux

I use the following pipe as variable in a script:
match=$( ls | grep -i "$search")
this is then used in an if statement:
if [ "$match" ]; then
echo "matches found"
else
echo "no matches found"
fi
what would be an alternative if I did not want to use find?
ShellCheck recommends:
ls /directory/target_file_pattern
but I do not get the syntax right to get the same result.
also I want no output when there are no matches for the if statement to work.

If you just want to tell if there exist any matches with bash you could use the builtin compgen like so:
if compgen -G 'glob_pattern_like_your_grep' >/dev/null; then
echo "matches found"
else
echo "no matches found"
fi
if you want to operate on the files that are matched, find is usually the right tool for the job:
find . -name 'glob_pattern_like_your_grep' -exec 'your command to operate on each file that matches'
the key though is that you have to use glob patterns, not regex type patterns.
If your find supports it, you might be able to match a regex like
find . -regex 'pattern'
and use that in either the if or with -exec

Use find:
match="$(find . -path "*${search}*" -printf "." | wc -c)"
$match will contain the number of matches. You can check it like this:
if [ "${match}" -gt 0 ] ; then
echo "${match} files found"
else
echo "No files found"
fi

Related

How to find whether any file of a directory contains a specific string using bash script?

I need to check if the string M1039C28 is inside any file of the directory /var/opt.
It should echo true if the string is found or echo String not found if the string is not found.
Sample:
cd /var/opt/;
if [ find ./ -type f -exec grep -Hni "M1039C28" {} ';']
then
echo "String found"
else
INFO "String not found"
fi
Consider using grep with options -q (suppress any output) and -r (recursive search in a directory):
grep -qr "search-query" /path/to/dir && echo "FOUND" || echo "NOT FOUND"
grep will exit with code 1 if it could not find the string in any files.
For more information, see the man page
Why don't you base your script on this command:
grep -l "M1039C28" * | wc -l
If the result is 0, then the entry is not found.
If the result is 1 or more, the entry is found.
If you have a large directory and want to search recursively, try using the silver searcher if not already installed, you can with on Debian sudo apt install silversearcher-ag or brew install the_silver_searcher on MacOS. Use it like this.
ag "M1039C28" path/to/search && echo "FOUND" || echo "NOT FOUND"
note path argument is optional, by default it searches current path recursively.

find and grep: get filenames

I need to find the reports (.docx files), read them with docx2txt, find the second match of "passed" (excluding "not passed") and save these filenames to text file. Here is what I tried:
OIFS="$IFS"
IFS=$'\n'
for f in $(find . -wholename '*_done/(*Report*.docx' |grep -v appendix)
do
docx2txt "$f" - | (grep -q -m2 passed || grep -q -v "not passed") || echo $f >> failed
done
IFS="$OIFS"
But this script gives me an empty file. If I replace || to && before echo, all filenames are stored into the file. grep works fine if it is not in the script, as well as docx2txt. What am I doing wrong here?
There are quite a lot problems with the grep commands.
grep -q always exits successfully on the first match.
With -q the -m2 has no effect. If there is one match grep exits successfully. It does not check if there is a second match.
To check that there are (at least) two matches, count the matches and then use test/[ ] to check the number of found matches. If there is at most one passed per line, grep -c is sufficient. If there can be multiple matches per line, you need grep -o ... | wc -l.
-q and -v together means: Is there at least one line that does not contain the pattern? When grep finds such a line it exits successfully. The only way for this command to fail is an input in which every line contains not passed (this includes the empty file).
Matching passed but not not passed is trickier than one might suspect. If there can be at most one passed/not passed per line, you can use grep -v 'not passed' | grep passed. Otherwise you need a need negative lookbehind, which is only available in perl compatible regular expressions (PCRE).
In addition to that command | (grep ... || grep ...) might not do what you expect. command produces output only once. After the first grep read some of this output, that read part is gone. The second grep will then continue reading where the first grep stopped.
BTW: for … in $(find … | grep -v …) can be turned into a single, safe find command using -not and -exec.
Solution
If each line contains at most one passed/not passed, use
find . -wholename '*_done/(*Report*.docx' -not -wholename '*appendix*' \
-exec sh -c '[ $(docx2txt "$0" - | grep -v "not passed" | grep -cm2 passed) = 2 ]' {} \; -print
If there can be multiple passed/not passed per line, you need GNU grep or pcregrep:
find . -wholename '*_done/(*Report*.docx' -not -wholename '*appendix*' \
-exec sh -c '[ $(docx2txt "$0" - | grep -Pom2 "(?<!not )passed" | wc -l) = 2 ]' {} \; -print
When you run into a problem like this, it's a good idea to remove as much code as possible. If we just take that one line with the multiple grep statements, we can first verify that the current expression doesn't work:
$ echo passed | ((grep -q -m2 passed || grep -q -v "not passed") || echo failed
$ echo not passed | ((grep -q -m2 passed || grep -q -v "not passed") || echo failed
We can see that neither of these commands produces at any output.
Let's think carefully about the logic:
The || operator means "if the first command doesn't succeed, run the second command". So in both cases, the first grep succeeds (because both passed and not passed contain the phrase passed). This means the second grep will never run, and it means that since the first command was successful, the entire grep ... || grep ... command will be successful, and that means the final echo $f will never run.
I was trying to think of a clever way to solve this, but it seems simplest if we make use of a temporary file:
OIFS="$IFS"
IFS=$'\n'
tmpfile=$(mktemp docXXXXXX)
trap "rm -f $tmpfile" EXIT
for f in $(find . -wholename '*_done/(*Report*.docx' |grep -v appendix)
do
docx2txt "$f" - | head -2 > $tmpfile
if grep -q passed $tmpfile && ! grep -q 'not passed' $tmpfile; then
echo $f >> failed
fi
done
IFS="$OIFS"

Linux shell script to search logs with user input

I am trying to write a script which searches a tomcat log from a keyword which the user inputs. I'm having issues getting the read command to work correctly.
#!/bin/bash
TOM1=/ap/mvr/servers/tomcat1/logs/
TOM2=/ap/mvr/servers/tomcat2/logs/
find $TOM1 $TOM2 -type f -name "mvr-gateway.log" | while read file
echo "What do you want to search for? " keyword
read keyword
do
LOGERRORS=$(grep -B1 -A1 $keyword $file)
if [[ ! -z $LOGERRORS ]]
then
echo $file
else echo No Data Found
fi
done
try this:
#!/bin/bash
TOM1=/ap/mvr/servers/tomcat1/logs/
TOM2=/ap/mvr/servers/tomcat2/logs/
read -p "What do you want to search for?" search_term
grep -B1 -A1 ${search_term} $(find $TOM1 $TOM2 -type f -name "mvr-gateway.log")
As mentioned before, ask first the term you are searching for.

How do I search for a file based on what is output by a command running on that file

I am working on a project for one of my professors and he asked me to sort a couple hundred .fits images based on their header files (specifically what star they are images of) I think that grep would be the best way to do this however I can't seam to figure out how to use grep based on the header.
I am entering:
ls | imhead *.fits | grep -E -r "PG\ 1104+243" *
to just list them out for now, once they are listed I know how to copy them into a directory.
I am new to using grep so I am unsure as to where my error lies? any help would be greatly appreciated! Thanks!
Assuming that imghead will extract the headers of the .fits as txt, you can use a simple shell script to do it:
script.sh
#!/bin/bash
grep "$1" "$2" > /dev/null 2>&1 && echo "$2"
Note that the + is a special character if you use extended regular expression, meaning if you pass the -E as in the question. A simple grep without any options should do the trick here.
Use find to exec the script on every *.fits file in the current folder:
find -maxdepth 1 -name '*.fits' -exec ./script.sh 'PG 1104+243' {} \;
If you are going to copy/move/alter or do something with the files you find, you might be better off, in terms of complexity and ease of quoting, using a loop like this:
#!/bin/bash
find . -name \*.fits -print0 | while read -d '' -r file; do
echo Checking file: $file
imhead "$file" | grep -q 'PG 1104+243'
if [ $? -eq 0 ]; then
echo Object matches: $file
fi
done

Directory checking in Linux

I am trying to write a script shell that takes two arguments as directory names, and determines if Directory 1 contains Directory 2 or vice versa. And also if there is no relationship between them.
I know the command to check if a directory exists is find -type d, however i was a bit confused as how to check and parse then names. I know i would need if-else loops, just not sure how to check for the conditions?
find won't be needed.
Something similar to this (but not guaranteeing directory name with spaces or some special characters.):
if [ "$dir1" == "$dir2" ]; then
echo "$dir1 == $dir2";
exit;
fi
if grep -E -q "^$dir2" <<< $dir1; then
echo "$dir1 is contained by $dir2."
exit
fi
if grep -E -q "^$dir1" <<< $dir2; then
echo "$dir2 is contained by $dir1.";
fi
However, this does not deal with symbolic links. For example, sym1 -> /usr/local/bin and sym2 -> /usr/local, apparently, sym2 contains sym1.
In addition, this does not deal with strange looking directory names, like /usr/local/./bin, which is the same as /usr/local/bin, or even /usr/local/../bin, which is the same as /usr/bin
--- Update ---
DevSolar metioned that readlink -e can be used to resolve the symbolic link. In my test, it also resolves the strange looking directory names like those with . and ... Thanks to DevSolar.
Do you want this?
if [ -d $1 ];then
a=`find $1 -type d -name $2`
if [ $a ];then
echo "$1 has $2"
else
echo "$1 does NOT has $2"
fi
fi
if [ -d $2 ];then
b=`find $2 -type d -name $1`
if [ $b ];then
echo "$2 has $1"
else
echo "$2 does NOT has $1"
fi
fi
This will do the trick I think,
find -name directory1 |grep directory2
or vice-versa, then use
echo $?
it will give 0 for success and 1 for failure.

Resources