sed with loop to replace certain fields in a file with delimiter : - shell

How should I use the sed command to replace certain fields with delimiter : and run a check to make sure that the user's input can be found within the file & if it can't be found it will loop again.
main_menu #function main_menu
echo "1) choice 1"
echo "2) choice 2"
read choice #read user choice on which choice he wants
if [ $choice -eq 1 ]
then
edit_item #function
read $choice_e #read input
grep -iqs "$choice_e: " Item.txt && echo "item found" #search file to find match
while [[ ! ${choice_e} =~ ^([Item.txt])$ ]]; do #loop to find if input matches search
echo "New Title: " #input new
read choice_n
sed -i 's/^/"$choice_n"\t/' Item.txt #edit the item
done
edit_item
else
echo "error" #return user to input again
fi

The invocation of sed is flawed because of the single quotes mixed with double quotes:
sed -i 's/^/"$choice_n"\t/'
The single quotes mean that the $ (and double quotes) are not interpreted by the shell. What you're probably after is:
sed -i "s/^/$choice_n\t/"
Without knowing exactly which shell and version of sed you're using, it isn't clear whether the \t sequence will be translated to a tab or not. In Bash, you could use the ANSI C Quoting mechanism:
sed -i "s/^/$choice_n"$'\t'/
I don't see where your 'delimiter :' is coming into play at all.

Related

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

read first line of the file and compare it with a string in shell file

I need to read first line of a file and match it with a text. If the text matches, I need to do certain operation.
Problem is if command is unable to compare the variable with the string.
file_content=$(head -1 ${file_name})
echo $file_content
if [[ $file_content = 'No new data' ]]; then
echo "Should come here"
fi
echo $file_content
if [ "${file_content}" = "No new data" ]; then
echo "Should come here"
fi
The if block is not working. I tried all possible syntax of if. I think the value that I am capturing in line 1 has some issues. Please help.
You could use
case $file_content in
(*No new data*)
echo "No news is good news."
;;
(*)
echo "Yay, breaking news."
esac
This would ignore any leading/trailing white space. This has the benefit of not forking expensive sed commands to just remove a few characters. That's like delivering a few sugar cubes with a flat bed truck...
There was trailing spaces.
Used the command
file_content="$(echo -e "${file_content_init}" | sed -e 's/^[[:space:]]//' -e 's/[[:space:]]$//')"
and then file_content was trimmed string that I used to compare.

Making flashcards with "select" command

So I have a file called "nouns" that looks like this:
English word:matching Spanish word
Englsih word:matching Spanish word
..etc etc
I need to make a program that list all the English words with an option to quit. The program displays the English words and ask the user for the word he wants translated and he can also type "quit" to exit.
This is what I have so far that shows me the list in English
select english in $(cut -d: -f1 nouns)
do
if [ "$english" = 'quit' ]
then
exit 0
fi
done
I know that I need to run a command that pulls up the second column (-f2) by searching for the corresponding English word like this
result=$(grep -w $english nouns|cut -d: -f2)
My end result should just out put the corresponding Spanish word. I am just not sure how to get all the parts to fit together. I know its based in a type of "if" format (I think) but do I start a separate if statement for the grep line?
Thanks
You need a loop in which you ask for input from user. The rest is putting things together with the correct control flow. See my code below:
while :
do
read -p "Enter word (or quit): " input
if [ "$input" = "quit" ]; then
echo "exiting ..."
break
else
echo "searching..."
result=$(grep $input nouns | cut -d ':' -f 2)
if [[ $result ]]; then
echo "$result"
else
echo "not found"
fi
fi
done
dfile=./dict
declare -A dict
while IFS=: read -r en es; do
dict[$en]=$es
done < "$dfile"
PS3="Select word>"
select ans in "${!dict[#]}" "quit program"; do
case "$REPLY" in
[0-9]*) w=$ans;;
*) w=$REPLY;;
esac
case "$w" in
quit*) exit 0;;
*) echo "${dict[$w]}" ;;
esac
done
You want to run this in a constant while loop, only breaking the loop if the user enters "quit." Get the input from the user using read to put it in a variable. As for the searching, this can be done pretty easily with awk (which is designed to work with delimited files like this) or grep.
#!/bin/sh
while true; do
read -p "Enter english word: " word
if [ "$word" = "quit" ]; then
break
fi
# Take your pick, either of these will work:
# awk -F: -v "w=$word" '{if($1==w){print $2; exit}}' nouns
grep -Pom1 "(?<=^$word:).*" nouns
done

How to remove contact from shell script?

I am creating a simple phonebook using unix shell scripts. I have gotten all of my functions to work except the removal of a contact after it has been created. I have tried combining grep and sed in order to accomplish this, but cannot seem to get over the hump. The removal shell i've tried is as follows.
#!/bin/sh
#removeContact.sh
echo “Remove Submenu”
echo “Please input First Name:”
read nameFirst
echo “Please input Last Name:”
read nameLast
x=$(grep -e “$nameFirst” -e “$nameLast” ContactList)
echo $x
sed '/'$x'/ d' ContactList;
echo “$nameFirst $nameLast is removed from your contacts”
exit 0
I'm not sure if I am declaring x incorrectly, or if my syntax is wrong when sed is used.
Any help would be greatly appreciated. Thank you.
#!/bin/bash
ContactList="contacts.txt"
export ContactList
exit=0
while [ $exit -ne 1 ]
do
echo "Main Menu"
echo "(a) Add a Contact"
echo "(r) Remove a Contact"
echo "(s) Search a Contact"
echo "(d) Display All Contact’s Information"
echo "(e) Exit"
echo "Your Choice?"
read choice
if [ "$choice" = "a" ]
then
./addContact.sh
elif [ "$choice" = "r" ]
then
./removeContact.sh
elif [ "$choice" = "s" ]
then
./searchContact.sh
elif [ "$choice" = "d" ]
then
./displayContact.sh
elif [ "$choice" = "e" ]
then
exit=1
else
echo "Error"
sleep 2
fi
done
exit 0
#!/bin/sh
#addContact.sh
ContactList="contacts.txt"
echo “Please input First Name:”
read nameFirst
echo “Please input Last Name:”
read nameLast
echo “Please input Phone Number:”
read number
echo “Please Input Address”
read address
echo “Please input Email:”
read email
echo $nameFirst:$nameLast:$number:$address:$email>> ContactList;
echo "A new contact is added to your book."
exit 0
sed '/'$x'/ d' ContactList
won't remove anything from the file ContactList, it will simply output the changes to standard output.
If you want to edit the file in-place, you'll need the -i flag (easy) or to make a temporary file which is then copied back over ContactList (not so easy, but needed if your sed has no in-place editing option).
In addition, since ContactList is a shell variable referencing the real file contacts.txt, you'll need to use $ContactList.
And, as a final note, since you're using the full line content to do deletion, the presence of an address like 1/15 Station St is going to royally screw up your sed command by virtue of the fact it contains the / character.
I would suggest using awk rather than sed for this task since it's much better suited to field-based data. With the record layout:
$nameFirst:$nameLast:$number:$address:$email
you could remove an entry with something like (including my patented paranoid perfect protection policy):
cp contacts.txt contacts.txt.$(date +%Y.%m.%d.%H.%M.%S_$$)
awk <contacts.txt >tmp.$$ -F: "-vF=$nameFirst" "-vL=$nameLast" '
F != $1 || L != $2 {print}'
mv tmp.$$ contacts.txt

Overwrite last line on terminal

My bash-script looks as following:
echo "Description:"
while [ $finishInput -eq 0 ]; do
read tmp
desc="$desc"$'\n'"$tmp"
if [ -z "$tmp" ]; then
finishInput="1"
fi
done
echo -n "Maintainer:"
read maintainer
It reads to the desc var until a empty line is passed. After that, i want to read in other stuff.
When executing my current script it looks like this:
Description:
Line 1
Line 2
Maintainer:
I would like to overwrite the last empty line with the "Maintainer:".
I searched for a solution but only found suggestions which were like
echo -n "Old line"
echo -e "\r new line"
which stays on the line and overwrites it. This is not possible in my case.
In your example you delete the text at the same line. When you want to return to the previous line use \e[1A, and to clear that line, use \e[K:
echo 'Old line'
echo -e '\e[1A\e[Knew line'
When you want to go N lines up, use \e[<N>A
Found a great guide on escape sequences and wanted to expand on some of the discussions here.
When you write out to a terminal, you move an invisible cursor around, much like you do when you write in any text editor. When using echo, it will automatically end the output with a new line character which moves the cursor to the next line.
$ echo "Hello" && echo " World"
Hello
World
You can use -n to prevent the new line and if you echo again after this, it will append it to the end of that line
$ echo -n "Hello" && echo " World"
Hello World
The cursor remains where it was so, on it's own, we can't use -n to overwrite the previous line, we need to move the cursor to the left. To do that we need to give it an escape sequence, which we let echo know we're going to use with -e and then move the cursor by providing a return carriage \r which puts the cursor at the beginning of the line.
$ echo -n "Hello" && echo -e "\rWorld"
World
That may look like it worked, but see what happens with
$ echo -n "A longer sentance" && echo -e "\rShort sentance"
Short sentancence
See the extra characters? Simply writing over the line only changes the characters where we wrote them.
To fix this, the accepted answer above uses the escape character \e[0K to erase everything after the cursor, after the cursor has moved left. i.e. \r move to beginning \e[0K erase to end.
$ echo -n "A longer sentance" && echo -e "\r\e[0KShort sentance"
Short sentance
Important \e to begin escape sequences works in zsh but not in sh and not necessarily in bash, however \033 works in all of them. If you want your script to work anywhere, you should preference \033
$ echo -n "A longer sentance" && echo -e "\r\033[0KShort sentance"
Short sentance
But escape characters can provide even more utility. For example \033[1A moves the cursor to the previous line so we don't need the -n on the previous echo:
$ echo "A longer sentance" && echo -e "\r\033[1A\033[0KShort sentance"
Short sentance
\r move to the beginning \033[1A move up \033[0K erase to the end
Finally, this is all a bit messy in my book, so you can turn this into a function:
overwrite() { echo -e "\r\033[1A\033[0K$#"; }
Using $# just puts all the parameters of the function into the string
$ echo Longer sentance && overwrite Short sentence
Short sentence
I built a function from Dennis Williamsons Comment:
function clearLastLine() {
tput cuu 1 && tput el
}
Thanks to Dennis Williamson
If you echo without the newline character echo -n "Something", you can use \r with your next echo to move the 'cursor' to the beginning of the line echo -e "\\rOverwrite something".
#!/bin/bash
CHECK_MARK="\033[0;32m\xE2\x9C\x94\033[0m"
echo -e "\n\e[4mDoing Things\e[0m"
echo -n "doing thing 1..."
sleep 1
echo -e "\\r${CHECK_MARK} thing 1 done"
Just be aware that if your new string is shorter that your old string, the tail of your old string will still be visible. Note the done.. in the gif above.
If you want to run a script in a loop and not blow up your scrollback, you can use the following pattern:
while sleep 10s; do
echo -n $(script)
echo -n -e "\e[0K\r"
done
Just replace the script command with your own.
#!/bin/bash
echo "Description:"
while test -z $finishInput; do
read -s tmp
desc="$desc"$'\n'"$tmp"
if [ -z "$tmp" ]; then
finishInput=1
else
echo $tmp
fi
#echo "fi="$finishInput;
done
echo -n "Maintainer:"
read maintainer
This solution avoids the empty line, but input is not echoed before the lines are complete.
Hint: My version of bash did not accept "[ $finishInput -eq 0 ]".

Resources