variables can't be used in grep pattern matching - shell

I'm trying to use a shell script to get the string from one file, then use it to get the matched sentence.
The script looks like this:
function find_str (){
echo $1
grep -e "\/$1" info.txt
}
for word in $(<./name.TXT); do
#egrep -w "$word" info.txt #this can't work either
find_str $word
done
It turns out that find_str $word cannot match some string like "/WORD1 balabala"
Any suggestion about this short piece of script?

What is the "/$1" part about? Are you looking for literal slashes? And do you need the function?
$ cat > info.txt
testing 123 dog
nothing matches a catalyst
doggerel is not poetry
my cat is a maine coone
$ cat > name.txt
dog cat
With -w and gnu grep, only lines with matching whole words are listed:
$ for word in $(<name.txt); do grep -w "$word" info.txt; done
testing 123 dog
my cat is a maine coone
Without the -w flag, all lines containing a match are listed:
$ for word in $(<name.txt); do grep "$word" info.txt; done
testing 123 dog
doggerel is not poetry
nothing matches a catalyst
my cat is a maine coone

Related

How to locate files that contain a specific word in a specific folder first, then replace it with a different one in Mac Terminal

i'm looking for a solution within Mac Terminal to first locate a specific word e.g. "testword" in a folder with a lot of text files in it and then replace the word with a different one.
I found the following line to locate a word in a folder with several text files in it:
grep -r 'testword' "path"
which works fine but i can't find a line to add to replace the word with another one in one combined command. Any suggestions?
Thanks a lot for your help! :)
For safety, I would suggest to do it in two steps. Using first grep, tr and sed to prepare all word replacements for each files. Check if all is okay, then execute all the sed remplacements.
$ touch hello123 helloABC helloXYZ hello000
$ echo "hello" > hello*
$ cat hello*
hello
hello
hello
hello
$ grep -lr "hello" . | tr "\n" " " | sed 's/^/sed \-i "s\/hello\/NewWord\/g" /g' | tee replace.sh
sed -i "s/hello/NewWord/g" ./helloXYZ ./helloABC ./hello000 ./hello123
$ sh replace.sh
$ cat hello*
NewWord
NewWord
NewWord
NewWord

Cut words from files based on grep

I have a small bash script as follows :
cat foo.txt | grep "balt" > bar_file
Ideally what I would like to happen is that every word that contains "balt", I would like removed from the foo.txt file. Can I get direction on how to basically move words from one file from another based on whats grepped.
As a side note: There is no need to use cat and pipe its output to grep since you can pass the filename directly to grep which reduces a single process execution.
As for your question you can -o option of grep to get matching words only having balt in them along with \b boundary checking like this:
$ cat foo.txt
abcd baltabcd xyz
xdef abbaltcd xyz
balt
$ grep -o '\b\w*balt\w*\b' foo.txt
baltabcd
abbaltcd
balt
$ grep -o '\b\w*balt\w*\b' foo.txt > bar_file
$ cat bar_file
baltabcd
abbaltcd
balt
$
As you can see grep matches 0 or more word characters present before or after balt and puts that into another file.
Example words were: baltabcd, abbaltcd and balt

How to make a script to make multiple grep's over a file?

I want to make a script that can do the following automatically:
grep 'string1' file.txt | grep 'string2' | grep 'string3' ... | grep 'stringN'
The idea is that the script can be run like this:
myScript.sh file.txt string1 string2 string3 ... stringN
and the script has to return all the lines of file.txt that contain all the strings.
For instance, if file.txt looks like this:
hello world
hello world run
hello planet world
And I can make a grep like this:
grep hello file.txt | grep world
and I get:
hello world
hello world run
hello planet world
I want to make a script that makes this automatically, with an undefined number of strings as parameters.
I found that it is hard to achieve this, since the number of strings can be variable. First, I tried to create an array called args like this in myScript.sh:
#!/bin/bash
args=("$#")
with the purpose of storing the arguments. I know that the ${args[0]} is going to be my file.txt and the rest are the strings that I need to use in the distinct greps, but I don't know how to proceed and if this is the best approach to solve the problem. I would appreciate any suggestion about how to program this.
sed is capable of doing this perfectly with a single process, and avoids these eval shenanigans. The resulting script is actually quite simple.
#!/bin/sh
file=$1
shift
printf '\\?%s?!d\n' "$#" |
sed -f - "$file"
We generate a line of sed script for each expression; if the expression is not (!) found, we delete (d) this input line, and start over with the next one.
This assumes your sed accepts - as the argument to -f to read the script from standard input. This is not completely portable; you would perhaps need to store the generated script in a temporary file instead if this is a problem.
This uses ? as the internal regex separator. If you need a literal ? in one of the patterns, you will need to backslash-escape it. In the general case, creating a script which finds an alternative separator which is in none of the search expressions would perhaps be possible, but at that point, I'd move to a proper scripting language (Python would be my preference) instead.
You can generate the pattern of operation and save it in a variable:
pattern="$(printf 'grep %s file.txt' "$1"; printf ' | grep %s' "${#:2}" ; printf '\n')"
and then
eval "$pattern"
Example:
% cat file.txt
foo bar
bar spam
egg
% grep_gen () { pattern="$(printf 'grep %s file.txt' "$1"; printf ' | grep %s' "${#:2}" ; printf '\n')"; eval "$pattern" ;}
% grep_gen foo bar
foo bar
You can create the command in a loop and then use eval to evaluate it.
This is using cat so you can group all the grep.
#! /bin/bash
file="$1"
shift
args=( "$#" )
cmd="cat '$file'"
for a in "${args[#]}"
do
cmd+=' | '
cmd+="grep '$a'"
done
eval $cmd
An eval-free alternative:
#!/bin/bash
temp1="$(mktemp)"
temp2="$(mktemp)"
grep "$2" "$1" > temp1
for arg in "${#:3}"; do
grep "$arg" temp1 > temp2
mv temp2 temp1
done
cat temp1
rm temp1
mktemp generates a temporary file with a unique name and returns its name; it should be widely available.
The loop then executes grep for each argument and renames the second temp file for the next loop.
This is the optimization of Diego Torres Milano's code and the answer to my original question:
#! /bin/bash
file=$1
shift
cmd="cat '$file'"
for 'a' in "$#"
do
cmd+=" | grep '$a'"
done
eval $cmd

Remove certain lines in string? Shell

Lets say I have a variable containing the following string (separated by blank spaces but when echoed it looks like lines):
animal:whale
animal:dog
animal:2_mice
animal:cat
And I have another variable containing the names of the animals I want to delete from the first string separated by spaces.
Lets say names="cat dog"
How would I go about deleting the lines that contain cat and dog from the first string?
I've tried looking up sed and grep approaches but haven't found any info that would work so far.
I should also point out that the expression needs to match exactly, so if the second variable contained just ca instead of cat, cat should not get deleted.
echo "$variable" | grep -Ev ":($(echo "$names" | sed 's/ /|/g'))$"
Using grep inverse search, assumes values in names variable is space separated and one word each:
$ echo "$var"
animal:whale
animal:dog
animal:2_mice
animal:cat
$ echo "$names"
cat dog
$ echo "$var" | grep -v $(printf ' -e :%s$' $names)
animal:whale
animal:2_mice

Print the contents of files from the output of a program

Let's say I have a program foo that finds files with a certain specification and that the output of running foo is:
file1.txt
file2.txt
file3.txt
I want to print the contents of each of those files (preferably with the file name prepended). How would I do this? I would've thought piping it to cat like so:
foo | cat
would work but it doesn't.
EDIT:
My solution to this problem prints out each file and prepends the filename to each line of output is:
foo | xargs grep .
This gets output similar to:
file1.txt: Hello world
file2.txt: My name is foobar.
<your command> | xargs cat
You need xargs here:
foo | xargs cat
In order to allow for file names that have spaces in them, you'll need something like this:
#/bin/bash
while read -r file
do
# Check for existence of the file before using cat on it.
if [[ -f $file ]]; then
cat "$file"
# Don't bother with empty lines
elif [[ -n $file ]]; then
echo "There is no file named '$file'"
fi
done
Put this a script. Let's call it myscript.sh. Then, execute:
foo | myscript.sh
foo | xargs grep '^' /dev/null
why grep on ^ ? to display also empty lines (replace with "." if you want only non-empty lines)
why is there a /dev/null ? so that, in addition to any filename provided in "foo" output, there is at least 1 additionnal file (and a file NOT maching anything, such as /dev/null). That way there is AT LEAST 2 filenames given to grep, and thus grep will always show the matching filename.

Resources