Get into a line from a text file and edit it - linux

Supposedly I have this line in my text file which has the following format.
"Title:Author:Price:QtyAvailable:QtySold"
The contents of my text file is shown below
Hello World:Andreas:10.50:10:5
Lord Of The Rings:Duke:50.15:50:20
(some other records...)
1) User inputs Author and Title.
2) If Program locates the Author+Title, it asks user to Update any of the fields available ( for this case Title, Author, Price etc..
3) For example I will like to update the Price of Hello World book.
4) What can I do to tell the program to extract the contents of the Hello World line, and get into 10.50 to replace the price of the book? (assumes new price of the book will be decided by the user's input)
Hope to get my answer.
Thanks in advance to those who helped!

You can use sed like this with variable parameters:
# book title:author to be searched
BOOK="Hello World:Andreas"
# price to be updated
PRICE=11.25
# search and update with original backed up with .bak extension
sed -i.bak 's/\('"$BOOK"'\):[^:]*:/\1:'"$PRICE"':/' file.txt
Explanation:
If you fill-in shell variables into sed command it will look like this:
sed 's/\(Hello World:Andreas\):[^:]*:/\1:11.25:/'
match
"$BOOK" # match literal text i.e. Hello World:Andreas
\($BOOK\) # group this text to be back referenced later
: # match literal :
[^:]* # match 0 or more characters until : is found
: # match a :
replacement
\1 # group # 1 i.e. Hello World:Andreas
: # a literal :
$PRICE # fill in the new price
: # literal :
Essentially this sed command is finding text that has Hello World:Andreas: followed by some price value and then followed by another :. Once this patter is found it is replacing that with back-reference # 1 (which is Hello World:Andreas) followed by a : and then put the new price value and a colon :.
EDIT: You are highly recommended to read some sed tutorial however as per your comment I am providing you command to update quantity:
# book title:author to be searched
BOOK="Hello World:Andreas"
# quantity to be updated
QTY=18
# search and update with original backed up with .bak extension
sed 's/^\('"$BOOK"'\):\([^:]*\):[^"]*:/\1:\2:'"$QTY"':/'

Following is a something to get you started:
Sample Script:
[jaypal:~/Temp] cat s.sh
#!/bin/bash
echo "Author?"
read author
echo "Title?"
read title
grep -c "$title:$author" file > /dev/null # Look for a line with matching values
if [ $? == 0 ]; then # If found then offer to change price
echo "I found the book, Do you want to update price to what?"
read newprice
sed -i "s/\($book:$author\):[^:]*:/\1:$newprice:/" file
fi
Input Data:
[jaypal:~/Temp] cat file
Hello World:Andreas:10.50:10:5
Lord Of The Rings:Duke:50.15:50:20
Execution:
[jaypal:~/Temp] ./s.sh
Author?
Andreas
Title?
Hello World
I found the book, Do you want to update price to what?
40
[jaypal:~/Temp] cat file
Hello World:Andreas:40:10:5
Lord Of The Rings:Duke:50.15:50:20

Related

How would I use strings inside a for loop?

I apologize for my question not being specific enough but I have no choice. So I received an assignment that hasn't been completely covered in the learning material (even the person assigned to help students is having trouble helping me) since this is beyond basic bash scripting. I'm not expecting anybody to do my assignment but if I can get a clue or an idea it'll be very helpful!
My assignment:
Code a script in bash linux that will use user's input of number of rows and number of columns, and print 'hello' strong according to the user's input, like so:
For example:
User's input of number of columns:2
User's input of number of rows: 3
hello hello
hello hello
hello hello
I thought in this direction but I can't figure it out and will appreciate any help :)
echo -e 'Please enter number of rows: \n'
read rows
echo -e 'Please enter number of columns: \n'
read columns
string='hello'
for i in $columns
do
echo $string
string+=$string
done
(this is as far as I got with the first loop as what ive done here doesn't work)
Check this out:
#!/bin/bash
read -p 'Please enter number of rows and columns: ' rows columns # prompt and read both vars at once
string='hello' # set string
printf -v row "%${columns}s" # create var $row consists on N(columns) spaces
row=${row//' '/"$string "} # recreate var $row changing spaces to "$string "
printf -v col "%${rows}s" # create var $col consists on N(rows) spaces
all=${col//' '/"$row\n"} # create full set in var $all by changing spaces to "$row\n"
printf "$all" # print all
Testing:
$ ./ex
Please enter number of rows and columns: 3 5
hello hello hello hello hello
hello hello hello hello hello
hello hello hello hello hello
With two loops:
#!/bin/bash
string='hello'
read -p "x:" x
read -p "y:" y
for ((j=0; j<$y; j++)); do
for ((i=0; i<$x; i++)); do
echo -n "$space$string"
space=" "
done
space=""
echo
done
See: man bash
To read inputs you can use read builtin. For example
read -r row column
Then you can use $row and $column variables.
You'd need a nested for loop to print row x column times.
To not print newlines, use -n option of echo.
Refer help read, help for, and help echo for details. You can obviously Google these terms, too ;-)
Do yo want to golf it? :)
printf "%$((rows*columns))s" | fold -w "$columns" | sed 's/ /hello /g'
To prompt the user for rows and colums, use the read builtin:
read -p 'Enter rows: ' rows
read -p 'Enter columns: ' columns
I prefer to get my arguments on the command line.
Accordingly, one implementation (with no error checking...):
rows=$1 # first arg is rows to output
cols=$2 # second column is columns wanted
str=$3 # third arg is the string to print
while (( rows-- )) # post-decrement rows
do c=$cols # reset a column count for each new row
while (( c-- )) # post-decrement columns done
do printf "%s " "$str" # print the string with a trailing space, NO newline
done
printf "\n" # print a newline at the end of each row
done
Make sure you understand ((...)) arithmetic processing, printf, and command line argument parsing. All these are available in the documentation.
For extra credit, do proper error checking of your inputs.
If you need to read the inputs from stdin instead of the command line, replace
rows=$1 # first arg is rows to output
cols=$2 # second column is columns wanted
str=$3 # third arg is the string to print
with
read rows cols str
Better, read each with an appropriate prompt - again, details available in the manual.
Good luck.

Extract information (subset) from a main files using a list of identifiers saved in another file

I have one file containing a list of name (refer as file 1):
Apple
Bat
Cat
I have another file (refer as file 2) containing a list of name and details refer:
Apple bla blaa
aaaaaaaaaggggggggggttttttsssssssvvvvvvv
ssssssssiiuuuuuuuuuueeeeeeeeeeennnnnnnn
sdasasssssssssssssssssssssswwwwwwwwwwww
Aeroplane dsafgeq dasfqw dafsad
vvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuus
fcsadssssssssssssssssssssssssssssssssss
ddddddddddddddddwwwwwwwwwwwwwwwwwwwwwww
sdddddddddddddddddddddddddddddwwwwwwwww
Bat sdasdas dsadw dasd
sssssssssssssssssssssssssssssssssssswww
ssssssssssssssssswwwwwwwwwwwwwwwwwwwwwf
aaaaaaaaaawwwwwwwwwwwwwwwwwwwwwwddddddd
sadddddddddddddddddd
Cat dsafw fasdsa dawwdwaw
sssssssssssssssssssssssssssssssssssssss
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwssss
I need to extract info out of file 2 using the list of names in file 1.
Output file should be something like below:
Apple bla blaa
aaaaaaaaaggggggggggttttttsssssssvvvvvvv
ssssssssiiuuuuuuuuuueeeeeeeeeeennnnnnnn
sdasasssssssssssssssssssssswwwwwwwwwwww
Bat sdasdas dsadw dasd
sssssssssssssssssssssssssssssssssssswww
ssssssssssssssssswwwwwwwwwwwwwwwwwwwwwf
aaaaaaaaaawwwwwwwwwwwwwwwwwwwwwwddddddd
sadddddddddddddddddd
Cat dsafw fasdsa dawwdwaw
sssssssssssssssssssssssssssssssssssssss
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwssss
Is there any commands for doing this using Linux (Ubuntu)? I am a new Linux user.
This might work for you (GNU sed):
sed 's#.*#/^&/bb#' file1 |
sed -e ':a' -f - -e 'd;:b;n;/^[A-Z]/!bb;ba' file2
Generate a string of sed commands from the first file and pipe them into another sed script which is run against the second file.
The first file creates a regexp for each line which when matched jumps to a piece of common code. If none of the regexps are matched the lines are deleted. If a regexp is matched then further lines are printed until a new delimiter is found at which point the code then jumps to the start and the process is repeated.
$ awk 'NR==FNR{a[$1];next} NF>1{f=($1 in a)} f' file1 file2
Apple bla blaa
aaaaaaaaaggggggggggttttttsssssssvvvvvvv
ssssssssiiuuuuuuuuuueeeeeeeeeeennnnnnnn
sdasasssssssssssssssssssssswwwwwwwwwwww
Bat sdasdas dsadw dasd
sssssssssssssssssssssssssssssssssssswww
ssssssssssssssssswwwwwwwwwwwwwwwwwwwwwf
aaaaaaaaaawwwwwwwwwwwwwwwwwwwwwwddddddd
sadddddddddddddddddd
Cat dsafw fasdsa dawwdwaw
sssssssssssssssssssssssssssssssssssssss
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwssss
Taking into consideration that each section has to be separated by an empty line, this solution with awk works ok:
while read -r pat;do
pat="^\\\<${pat}\\\>"
awk -vpattern=$pat '$0 ~ pattern{p=1}$0 ~ /^$/{p=0}p==1' file2
done <file1
This solution to work , requires the file to like this:
Apple bla blaa
1 aaaaaaaaaggggggggggttttttsssssssvvvvvvv
2 ssssssssiiuuuuuuuuuueeeeeeeeeeennnnnnnn
3 sdasasssssssssssssssssssssswwwwwwwwwwww
Aeroplane dsafgeq dasfqw dafsad
4 vvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuus
5 fcsadssssssssssssssssssssssssssssssssss
6 ddddddddddddddddwwwwwwwwwwwwwwwwwwwwwww
7 sdddddddddddddddddddddddddddddwwwwwwwww
Bat sdasdas dsadw dasd
8 sssssssssssssssssssssssssssssssssssswww
9 ssssssssssssssssswwwwwwwwwwwwwwwwwwwwwf
10 aaaaaaaaaawwwwwwwwwwwwwwwwwwwwwwddddddd
11 sadddddddddddddddddd
Cat dsafw fasdsa dawwdwaw
12 sssssssssssssssssssssssssssssssssssssss
13 wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwssss
PS: Numbering has been applied by me in order to be able to "check" that awk will return the correct results per section. Numbering is not required in your real file.
If there are not empty lines separating each section then it is much harder to achieve the correct result.

find a pattern and print line based on finding the first pattern sed, awk grep

I have a rather large file. What is common to all is the hostname to break each section example :
HOSTNAME:host1
data 1
data here
data 2
text here
section 1
text here
part 4
data here
comm = 2
HOSTNAME:host-2
data 1
data here
data 2
text here
section 1
text here
part 4
data here
comm = 1
The above prints
As you see above, in between each section there are other sections broken down by key words or lines that have specific values
I like to use a oneliner to print host name for each section and then print which ever lines I want to extract under each hostname section
Can you please help. I am using now grep -C 10 HOSTNAME | gerp -C pattern
but this assumes that there are 10 lines in each section. This is not an optimal way to do this; can someone show a better way. I also need to be able to print more than one line under each pattern that I find . So if I find data1 and there are additional lines under it I like to grab and print them
So output of command would be like
grep -C 10 HOSTNAME | grep data 1
grep -C 10 HOSTNAME | grep -A 2 data 1
HOSTNAME:Host1
data 1
HOSTNAME:Hoss2
data 1
Beside Grep I use this sed command to print my output
sed -r '/HOSTNAME|shared/!d' filename
The only problem with this sed command is that it only prints the lines that have patterns shared & HOSTNAME in them. I also need to specify the number of lines I like to print in my case under the line that matched patterns shared. So I like to print HOSTNAME and give the number of lines I like to print under second search pattern shared.
Thanks
awk to the rescue!
$ awk -v lines=2 '/HOSTNAME/{c=lines} NF&&c&&c--' file
HOSTNAME:host1
data 1
HOSTNAME:host-2
data 1
print lines number of lines including pattern match, skips empty lines.
If you want to specify secondary keyword instead number of lines
$ awk -v key='data 1' '/HOSTNAME/{h=1; print} h&&$0~key{print; h=0}' file
HOSTNAME:host1
data 1
HOSTNAME:host-2
data 1
Here is a sed twoliner:
sed -n -r '/HOSTNAME/ { p }
/^\s+data 1/ {p }' hostnames.txt
It prints (p)
when the line contains a HOSTNAME
when the line starts with some whitespace (\s+) followed by your search criterion (data 1)
non-mathing lines are not printed (due to the sed -n option)
Edit: Some remarks:
this was tested with GNU sed 4.2.2 under linux
you dont need the -r if your sed version does not support it, replace the second pattern to /^.*data 1/
we can squash everything in one line with ;
Putting it all together, here is a revised version in one line, without the need for the extended regex ( i.e without -r):
sed -n '/HOSTNAME/ { p } ; /^.*data 1/ {p }' hostnames.txt
The OP requirements seem to be very unclear, but the following is consistent with one interpretation of what has been requested, and more importantly, the program has no special requirements, and the code can easily be modified to meet a variety of requirements. In particular, both search patterns (the HOSTNAME pattern and the "data 1" pattern) can easily be parameterized.
The main idea is to print all lines in a specified subsection, or at least a certain number up to some limit.
If there is a limit on how many lines in a subsection should be printed, specify a value for limit, otherwise set it to 0.
awk -v limit=0 '
/^HOSTNAME:/ { subheader=0; hostname=1; print; next}
/^ *data 1/ { subheader=1; print; next }
/^ *data / { subheader=0; next }
subheader && (limit==0 || (subheader++ < limit)) { print }'
Given the lines provided in the question, the output would be:
HOSTNAME:host1
data 1
HOSTNAME:host-2
data 1
(Yes, I know the variable 'hostname' in the awk program is currently unused, but I included it to make it easy to add a test to satisfy certain obvious requirements regarding the preconditions for identifying a subheader.)
sed -n -e '/hostname/,+p' -e '/Duplex/,+p'
The simplest way to do it is to combine two sed commands ..

How to remove all lines from a text file starting at first empty line?

What is the best way to remove all lines from a text file starting at first empty line in Bash? External tools (awk, sed...) can be used!
Example
1: ABC
2: DEF
3:
4: GHI
Line 3 and 4 should be removed and the remaining content should be saved in a new file.
With GNU sed:
sed '/^$/Q' "input_file.txt" > "output_file.txt"
With AWK:
$ awk '/^$/{exit} 1' test.txt > output.txt
Contents of output.txt
$ cat output.txt
ABC
DEF
Walkthrough: For lines that matches ^$ (start-of-line, end-of-line), exit (the whole script). For all lines, print the whole line -- of course, we won't get to this part after a line has made us exit.
Bet there are some more clever ways to do this, but here's one using bash's 'read' builtin. The question asks us to keep lines before the blank in one file and send lines after the blank to another file. You could send some of standard out one place and some another if you are willing to use 'exec' and reroute stdout mid-script, but I'm going to take a simpler approach and use a command line argument to let me know where the post-blank data should go:
#!/bin/bash
# script takes as argument the name of the file to send data once a blank line
# found
found_blank=0
while read stuff; do
if [ -z $stuff ] ; then
found_blank=1
fi
if [ $found_blank ] ; then
echo $stuff > $1
else
echo $stuff
fi
done
run it like this:
$ ./delete_from_empty.sh rest_of_stuff < demo
output is:
ABC
DEF
and 'rest_of_stuff' has
GHI
if you want the before-blank lines to go somewhere else besides stdout, simply redirect:
$ ./delete_from_empty.sh after_blank < input_file > before_blank
and you'll end up with two new files: after_blank and before_blank.
Perl version
perl -e '
open $fh, ">","stuff";
open $efh, ">", "rest_of_stuff";
while(<>){
if ($_ !~ /\w+/){
$fh=$efh;
}
print $fh $_;
}
' demo
This creates two output files and iterates over the demo data. When it hits a blank line, it flips the output from one file to the other.
Creates
stuff:
ABC
DEF
rest_of_stuff:
<blank line>
GHI
Another awk would be:
awk -vRS= '1;{exit}' file
By setting the record separator RS to be an empty string, we define the records as paragraphs separated by a sequence of empty lines. It is now easily to adapt this to select the nth block as:
awk -vRS= '(FNR==n){print;exit}' file
There is a problem with this method when processing files with a DOS line-ending (CRLF). There will be no empty lines as there will always be a CR in the line. But this problem applies to all presented methods.

Finding the pattern and replacing the pattern inside the file using unix

I need your help in unix.i have a file where i have a value declared and and i have to replace the value when called. for example i have the value for &abc and &ccc. now i have to substitute the value of &abc and &ccc in the place of them as shown in the output file.
Input File
go to &abc=ddd;
if file found &ccc=10;
no the value name is &abc;
and the age is &ccc;
Output:
go to &abc=ddd;
if file found &ccc=10;
now the value name is ddd;
and the age is 10;
Try using sed.
#!/bin/bash
# The input file is a command line argument.
input_file="${1}"
# The map of variables to their values
declare -A value_map=( [abc]=ddd [ccc]=10 )
# Loop over the keys in our map.
for variable in "${!value_map[#]}" ; do
echo "Replacing ${variable} with ${value_map[${variable}]} in ${input_file}..."
sed -i "s|${variable}|${value_map[${variable}]}|g" "${input_file}"
done
This simple bash script will replace abc with ddd and ccc with 10 in the given file. Here is an example of it working on a simple file:
$ cat file.txt
so boo aaa abc
duh
abc
ccc
abcccc
hmm
$ ./replace.sh file.txt
Replacing abc with ddd in file.txt...
Replacing ccc with 10 in file.txt...
$ cat file.txt
so boo aaa ddd
duh
ddd
10
ddd10
hmm

Resources