Find and replace few words in text file with using bash - bash

I have a script to, where in one variable words, that i have in file, in other variable, i have words, that i want use instead words from first variable. I need to find i am scatman and replace these words to you are dukenukem. For example, my text file, wwe.txt:
i
am
dsadsa
sda
daaaa
ds
dsds
dsa
d
scatman
For example, i wrote script, that makes grep, and it works:
words="i am scatman"
echo "$words"
for i in $words; do
if grep -q "$i" wwe.txt; then
echo "these words are exists"
grep "$i" wwe.txt
else
echo "these words are not exists"
exit 1
fi
done
It works. But if i want, to replace these words, how i can do this ? i wrote this:
words="i am scatman"
words2="you are dukenukem"
for i in $words; do
for y in $words2; do
if grep -q "$i" wwe.txt; then
echo "these words are exists"
grep "$i" wwe.txt
sed -i 's/'"$i"'/'"$y"'/g' wwe.txt
else
echo "these words are not exists"
exit 1
fi
done
done
But it does not work, where i have error ? Help please.

This code works. Please try it out.
#!/bin/bash
line1="i am scatman"
line2="you are dukenukem"
words2=($line2)
count=0
for word in $line1; do
sed -i -e "s/$word/${words2[$count]}/g" wwe.txt
count=$((count + 1))
done

Related

Finding presence of substring within a string in BASH

I have a script that is trying to find the presence of a given string inside a file of arbitrary text.
I've settled on something like:
#!/bin/bash
file="myfile.txt"
for j in `cat blacklist.txt`; do
echo Searching for $j...
unset match
match=`grep -i -m1 -o "$j" $file`
if [ $match ]; then
echo "Match: $match"
fi
done
Blacklist.txt contains lines of potential matches, like so:
matchthis
"match this too"
thisisasingleword
"This is multiple words"
myfile.txt could be something like:
I would matchthis if I could match things with grep. I really wish I could.
When I ask it to match this too, it fails to matchthis. It should match this too - right?
If I run this at a bash prompt, like so:
j="match this too"
grep -i -m1 -o "$j" myfile.txt
...I get "match this too".
However, when the batch file runs, despite the variables being set correctly (verified via echo lines), it never greps properly and returns nothing.
Where am I going wrong?
Wouldn't
grep -owF -f blacklist.txt myfile.txt
instead of writing an inefficient loop, do what you want?
Would you please try:
#!/bin/bash
file="myfile.txt"
while IFS= read -r j; do
j=${j#\"}; j=${j%\"} # remove surrounding double quotes
echo "Searching for $j..."
match=$(grep -i -m1 -o "$j" "$file")
if (( $? == 0 )); then # if match
echo "Match: $match" # then print it
fi
done < blacklist.txt
Output:
Searching for matchthis...
Match: matchthis
Searching for match this too...
Match: match this too
match this too
Searching for thisisasingleword...
Searching for This is multiple words...
I wound up abandoning grep entirely and using sed instead.
match=`sed -n "s/.*\($j\).*/\1/p" $file
Works well, and I was able to use unquoted multiple word phrases in the blacklist file.
With this:
if [ $match ]; then
you are passing random arguments to test. This is not how you properly check for variable net being empty. Use test -n:
if [ -n "$match" ]; then
You might also use grep's exit code instead:
if [ "$?" -eq 0 ]; then
for ... in X splits X at spaces by default, and you are expecting the script to match whole lines.
Define IFS properly:
IFS='
'
for j in `cat blacklist.txt`; do
blacklist.txt contains "match this too" with quotes, and it is read like this by for loop and matched literally.
j="match this too" does not cause j variable to contain quotes.
j='"match this too"' does, and then it will not match.
Since whole lines are read properly from the blacklist.txt file now, you can probably remove quotes from that file.
Script:
#!/bin/bash
file="myfile.txt"
IFS='
'
for j in `cat blacklist.txt`; do
echo Searching for $j...
unset match
match=`grep -i -m1 -o "$j" "$file"`
if [ -n "$match" ]; then
echo "Match: $match"
fi
done
Alternative to the for ... in ... loop (no IFS= needed):
while read; do
j="$REPLY"
...
done < 'blacklist.txt'

How I prepend a string on each item of an array?

I have the following bash script:
#!/bin/bash
items=('mysql_apache','postgresql_apache','maria_apache')
string=""
for i in "${array[#]}"; do
string=$string" -t"$i
done
echo $string
But if I output the string I won't get the expected result:
-t 'mysql_apache' -t 'postgresql_apache' -t 'maria_apache'
DO you have any Idea how I can do this?
Edit 1
I tried the following:
#!/bin/bash
items=('mysql_apache' 'postgresql_apache' 'maria_apache')
string=""
for i in "${array[#]}"; do
string=$string" -t"$i
done
echo $string
But I still do not get the expected output.
Array elements are separated by whitespace, not commas. Also, items != array.
#! /bin/bash
items=(mysql_apache postgresql_apache maria_apache)
string=""
for i in "${items[#]}"; do
string+=" -t $i"
done
echo $string
But you don't need a loop at all:
items=(mysql_apache postgresql_apache maria_apache)
echo ${items[#]/#/-t }
The substitution can be applied to every element of an array. The /# matches at the start of each string.
You're close. Your forgot to change ${array[#]} in the for loop to what your array was named: items or specifically ${items[#]} You also needed a few other little changes, see below:
#!/bin/bash
declare -a items=('mysql_apache' 'postgresql_apache' 'maria_apache')
string=""
for i in "${items[#]}"; do
string=${string}" -t "$i
done
echo $string
Lastly if you want to see what is happening you can add temporary echo statements to see what if anything is changing:
for i in "${items[#]}"; do
string=${string}" -t "$i
echo >>>$string<<<
done

Command to trim file names to given amount of letters in bash

Im trying to make a simple script to trim all files names in directory where the script is opened to given amount of letters in bash.
Example of script
To how many letters do you want to trim files?
User: 3
Adsbgnyr.txt > Ads.txt
echo "What do you want to do? Changes will be done to all files in current catalog"
echo "1. Change files names to uppercase"
echo "2. Change files names to lowercase"
echo "3. Trim files names to give amount of letters"
echo "4. Exit"
echo "option"
read option
if [ $option = "1" ]
then
for i in $( ls | grep [a-z] )
do
mv -i $i `echo $i | tr 'a-z' 'A-Z'`
done
fi
if [ $option = "2" ]
then
for g in $( ls | grep [A-Z] )
do
mv -i $g `echo $g | tr 'A-Z' 'a-z'`
done
fi
if [ $option = "3" ]
then
echo "o how many letters do you want to trim files?"
read howmany
>>THIS IS THE PLACE WHERE I WANT TO PUT THE COMMAND<<
fi
if [ $option = "4" ]
then
exit
fi
Try in this way
shopt -s nullglob
for h in *
do
echo ${h:0:$howmany}
done
some code review suggestions:
indentation is important
quote your variables
don't parse ls
use select for menus
The answer to your question can be found in the bash manual: Shell Parameter Expansion
choices=(
"Change files names to uppercase"
"Change files names to lowercase"
"Trim files names to give amount of letters"
"Exit"
)
echo "Changes will be done to all files in current catalog:"
PS3="What do you want to do? "
select choice in "${choices[#]}"; do
case $REPLY in
1) for file in *[a-z]*; do
mv -i "$file" "${file^^}"
done
;;
2) for file in *[A-Z]*; do
mv -i "$file" "${file,,}"
done
;;
3) read -p "To how many letters do you want to trim files?" howmany
for file in *; do
mv -i "$file" "${file:0:howmany}"
done
;;
4) exit ;;
esac
done
Note there's no $ for "howmany" in ${file:0:howmany} -- bash lets you omit the $ for variables in arithmetic expressions, and both the offset and length are arithmetic expressions in the ${var:offset:length} expansion.
What are you going to do if the user trims the files too small: if filename1 and filename2 exist, and $howmany = 8 then filename1 will be lost after mv filename2 filename

How to make multiple search and replace in files via bash

i have script. In this script i made search and replace of words. Word by word until word 'end'. It is ok and it works. You can see body of my script:
#!/bin/bash
end=end
until [ "$first" = "$end" ];do
echo "please write first word";
read first
if grep -q "$first" *txt; then
echo "word is exists"
grep "$first" *txt
echo "please write second word";
read second
sed -i 's/'"$first"'/'"$second"'/g' *txt
else
echo "second word does not exists"
exit 1
fi
done
It works for me. I have in the result console, where I can endlessly loop words, but if i want to do something like this: How can i write multiple words in line.
For example: "dog" "cat" "fish"
And search and replace all of these words. How can do it? For example, if i need to replace on these words ("elephat" "mouse" "bird"). How can you do it?
I mean search and replace words, like arguments.
You just need a loop to process the arguments.
Assuming you run the script passing pairs of original replacement words (myscript.sh original_word1 replacement1 original_word2 replacement2 ...) it would be something like the following:
while [[ $# -gt 1 ]]
do
original="$1"
replacement="$2"
# your code for actually replacing $original with $replacement
shift # discard already processed original arg
shift # discard already processed replacement arg
done
Note that if the user passes a last original word without replacement the script will just ignore it
Your English is rough, but I think you want to be able to prompt for multiple words, and replace them with a new set?
The below code will let you run a program like replace_words one two three and then be prompted for a list of words to replace, e.g. 1 2 3. After that, it exits.
declare -a replace_list=( "$#" ) # get the replace list as passed arguments
echo -n "Enter words to replace with: "; read -ra sub_list
for ((i=0; i < "${#replace_list[#]}"; ++i)); do
if grep -q "${replace_list[$i]}" *txt; then
echo "first word is exists"
sed -i "s/${replace_list[$i]}/${sub_list[$i]}/g" *txt
else
echo "${replace_list[$i]} does not exists"
exit 1
fi
done

I want a to compare a variable with files in a directory and output the equals

I am making a bash script where I want to find files that are equal to a variable. The equals will then be used.
I want to use "mogrify" to shrink a couple of image files that have the same name as the ones i gather from a list (similar to "dpkg -l"). It is not "dpkg -l" I am using but it is similar. My problem is that it prints all the files not just the equals. I am pretty sure this could be done with awk instead of a for-loop but I do not know how.
prog="`dpkg -l | awk '{print $1}'`"
for file in $dirone* $dirtwo*
do
if [ "basename ${file}" = "${prog}" ]; then
echo ${file} are equal
else
echo ${file} are not equal
fi
done
Could you please help me get this working?
First, I think there's a small typo. if [ "basename ${file}" =... should have backticks inside the double quotes, just like the prog=... line at the top does.
Second, if $prog is a multi-line string (like dpkg -l) you can't really compare a filename to the entire list. Instead you have to compare one item at a time to the filename.
Here's an example using dpkg and /usr/bin
#!/bin/bash
progs="`dpkg -l | awk '{print $2}'`"
for file in /usr/bin/*
do
base=`basename ${file}`
for prog in ${progs}
do
if [ "${base}" = "${prog}" ]; then
echo "${file}" matches "${prog}"
fi
done
done
The condition "$file = $prog" is a single string. You should try "$file" = "$prog" instead.
The following transcript shows the fix:
pax> ls -1 qq*
qq
qq.c
qq.cpp
pax> export xx=qq.cpp
pax> for file in qq* ; do
if [[ "${file} = ${xx}" ]] ; then
echo .....${file} equal
else
echo .....${file} not equal
fi
done
.....qq equal
.....qq.c equal
.....qq.cpp equal
pax> for file in qq* ; do
if [[ "${file}" = "${xx}" ]] ; then
echo .....${file} equal
else
echo .....${file} not equal
fi
done
.....qq not equal
.....qq.c not equal
.....qq.cpp equal
You can see in the last bit of output that only qq.cpp is shown as equal since it's the only one that matches ${xx}.
The reason you're getting true is because that's what non-empty strings will give you:
pax> if [[ "" ]] ; then
echo .....equal
fi
pax> if [[ "x" ]] ; then
echo .....equal
fi
.....equal
That's because that form is the string length checking variation. From the bash manpage under CONDITIONAL EXPRESSIONS:
string
-n string
True if the length of string is non-zero.
Update:
The new code in your question won't quite work as expected. You need:
if [[ "$(basename ${file})" = "${prog}" ]]; then
to actually execute basename and use its output as the first part of the equality check.
you can use case/esac
case "$file" in
"$prog" ) echo "same";;
esac

Resources