I'm having problems creating a sed script - linux

My teacher didn't really go over sed scripts so they're very confusing. I need to finish this to get an A though. It's due very soon so I doubt I have time to fully understand it because I don't understand the syntax at all.
The instructions are:
Create a sed script named script3 that will print a file with “The Raven” at the top, replace every occurrence of multiple spaces with a single space, and print a line of 30 dashes below each line.
This is what I have so far:
sed script
echo "The Raven"
s/[ ]\{2,\}/ /g
/\,\./ s/^/------------------------------ /

Using the online GNU sed manual:
use the i command with an address to insert the title
use the a command with no address to append the separator after every line
use s/[[:blank:]]\+/ /g to replace any horizontal whitespace characters with a single space: sed regular expressions

enter code hereCreate a sed script named script3
so create a text file calles script3 taht is called with sed -f script3 YourSoureFile
content is
1 i\
The raven
s/ \{2,\}/ /g
a\
------------------------------
that will print a file with
“The Raven” at the top
1 i\
The raven
on 1st (1) line, insert (i) the next line (each line following until last cahr is NO NORE \
replace every occurrence of multiple spaces with a single space
s/ \{2,\}/ /g
substitute (s///) any pattern (part of text between the separator /composed of 2 or more \{2,\} following space jsute before the occurance specification, by a singel space the second , any occurence present ( option g). This happend at each line (no number or filter pattern in the head of the line like the 1 on precedent line)
and print a line of 30 dashes below each line
a\
------------------------------
(a) work like the (i) but append in this case. No filter pattern nor number indicate line to act, so on each line also.
i and a add text to ouptut but not on working buffer, so this cannot be manipulate inside this sed action, this is not the case of s/// that work ON the current working buffer (that is print at end of treatment of the line, before starting with a new line)
This is what I have so far:
About your script
echo "The Raven"
that is not a sed action but a shell action so, it cannot work in a sed like this (could using shell substitution but not the goal normaly)
s/[ ]\{2,\}/ /g
it's fine, class [] for this space is not necessary here but litterally ok. You could use [[:space:]] or [[:blank:]] or [[:space:][:blank:]] to be exhaustif using meta class of type [:xxxxx:] inside a class
/\,\./ s/^/------------------------------ /
this will mean *on each occurence of ,. (both and in this order), replace (add thus) the start of the line with 30 - and a space. So it's not occuring on each line and it add at the start not following the line. A way using s/// could be:
s/.*/&\
------------------------------/
Replace on each line every char (all at once) by herself (&) followed by a new line and 30 dash

Related

How to add character at the end of specific line in UNIX/LINUX?

Here is my input file. I want to add a character ":" into the end of lines that have ">" at the beginning of the line. I tried seq -i 's|$|:|' input.txt but ":" was added to all the ending of each line. It is also hard to call out specific line numbers because, in each of my input files, the line contains">" present in different line numbers. I want to run a loop for multiple files so it is useless.
>Pas_pyrG_2
AAAGTCACAATGGTTAAAATGGATCCTTATATTAATGTCGATCCAGGGACAATGAGCCCA
TTCCAGCATGGTGAAGTTTTTGTTACCGAAGATGGTGCAGAAACAGATCTGGATCTGGGT
>Pas_rpoB_4
CAAACTCACTATGGTCGTGTTTGTCCAATTGAAACTCCTGAAGGTCCAAACATTGGTTTG
ATCAACTCGCTTTCTGTATACGCAAAAGCGAATGACTTCGGTTTCTTGGAAACTCCATAC
CGCAAAGTTGTAGATGGTCGTGTAACTGATGATGTTGAATATTTATCTGCAATTGAAGAA
>Pas_cpn60_2
ATGAACCCAATGGATTTAAAACGCGGTATCGACATTGCAGTAAAAACTGTAGTTGAAAAT
ATCCGTTCTATTGCTAAACCAGCTGATGATTTCAAAGCAATTGAACAAGTAGGTTCAATC
TCTGCTAACTCTGATACTACTGTTGGTAAACTTATTGCTCAAGCAATGGAAAAAGTAGGT
AAAGAAGGCGTAATCACTGTAGAAGAAGGCTCAGGCTTCGAAGACGCATTAGACGTTGTA
Here is experted output file:
>Pas_pyrG_2:
AAAGTCACAATGGTTAAAATGGATCCTTATATTAATGTCGATCCAGGGACAATGAGCCCA
TTCCAGCATGGTGAAGTTTTTGTTACCGAAGATGGTGCAGAAACAGATCTGGATCTGGGT
>Pas_rpoB_4:
CAAACTCACTATGGTCGTGTTTGTCCAATTGAAACTCCTGAAGGTCCAAACATTGGTTTG
ATCAACTCGCTTTCTGTATACGCAAAAGCGAATGACTTCGGTTTCTTGGAAACTCCATAC
CGCAAAGTTGTAGATGGTCGTGTAACTGATGATGTTGAATATTTATCTGCAATTGAAGAA
>Pas_cpn60_2:
ATGAACCCAATGGATTTAAAACGCGGTATCGACATTGCAGTAAAAACTGTAGTTGAAAAT
ATCCGTTCTATTGCTAAACCAGCTGATGATTTCAAAGCAATTGAACAAGTAGGTTCAATC
TCTGCTAACTCTGATACTACTGTTGGTAAACTTATTGCTCAAGCAATGGAAAAAGTAGGT
AAAGAAGGCGTAATCACTGTAGAAGAAGGCTCAGGCTTCGAAGACGCATTAGACGTTGTA
Do seq have more option to modify or the other commands can solve this problem?
sed -i '/^>/ s/$/:/' input.txt
Search the lines of input for lines that match ^> (regex for "starts with the > character). Those that do substitute : for end-of-line (you got this part right).
/ slashes are the standard separator character in sed. If you wish to use different characters, be sure to pass -e or s|$|:| probably won't work. Since / characters, unlike | characters, are not meaningful character within the shell, it's best to use them unless the pattern also contains slashes, in which case things get unwieldy.
Be careful with sed -i. Make a backup - make sure you know what's changing by using diff to compare the files.
On OSX -i requires an argument.
Using ed to edit the file:
printf "%s\n" 'g/^>/s/$/:/' w | ed -s input.txt
For every line starting with >, add a colon to the end, and then write the changed file back to disk.

grep obtains pattern from a file but printing not only the whole match word

I've got file.txt to extract lines containing the exact words listed in check.txt file.
# file.txt
CA1C 2637 green
CA1C-S1 2561 green
CA1C-S2 2371 green
# check.txt
CA1C
I tried
grep -wFf check.txt file.txt
but I'm not getting the desired output, i.e. all the three lines were printed.
Instead, I'd like to get only the first line,
CA1C 2637 green
I searched and found this post being relevant, it's easy to do it when doing only one word matching. But how can I improve my code to let grep obtain patterns from check.txt file and print only the whole word matched lines?
A lot of thanks!
The man page for grep says the following about the -w switch:
-w, --word-regexp
Select only those lines containing matches that form whole words. The test is that the matching substring must either be at the beginning of the line, or preceded by a non-word constituent character. Similarly, it must be either at the end of the line or followed by a non-word constituent character. Word-constituent characters are letters, digits, and the underscore.
In your case, all three lines start with "CA1C-", which meets the conditions of being at the beginning of the line, and being followed by a non-word constituent character (the hyphen).
I would do this with a loop, reading lines manually from check.txt:
cat check.txt | while read line; do grep "^$line " file.txt; done
CA1C 2637 green
This loop reads the lines from check.txt, and searches for each one at the start of a line in file.txt, with a following space.
There may be a better way to do this, but I couldn't get -f to actually consider whitespace at the end of a line of the input file.

update the first character of line if line contains two strings

I am looking for a script to search a line if it contains two strings and then find if the first charater of that line contains certain character and remove it.
Eg. if line contains two strings as "abc" and "xyz" it should look for first character of the line and if it contains # , it should remove it and vice-versa.
It tried to run below command in crontab and got the result
crontab -l | grep az2-er32-cxv-iz| grep aze
Output
#5,10 * * * * /opt/apps/scripts/dsm-rync -q -del -s az2-er32-cxv-iz /opt/apps/sdl/scripts/aze-dsm-rync.app.config
since , its difficult to update the crontab entry directly , i copied it to tmpfile.
crontab -l > tmpfile and tried to run sed 's/^#//' tmpfile but it is removing all # instead of the line matching with two strings
You may use gnu awk to do this easily:
awk -i inplace '/az2-er32-cxv-iz/ && /aze/{sub(/^#/, "")} 1' crontab
This will remove # from first position if line has az2-er32-cxv-iz and aze in it.
As mentioned by Shloim, I won't give you the code itself, but I'm giving you a piece of pseudo-code to start:
In order to know if a line contains at least two words, you might search for a space within that line. (grep " " <filename>)
In order to know if a line starts with a certain character, you might search for the character, followed by that one certain character. (grep "<beginning_of_line>#")
Replacing a character in a line can be done, using the sed command.

Sed command with exact variable change

I want to replace exact word by sed command with variable. My file looks like this:
//module xyz
module xyz
Suppose I have the following shell variables defined:
var1='module xyz'
var2='module abc'
I want to change xyz to abc in uncommented line only(module xyz)
So after executing command output should be
//module xyz
module abc
I do not want to change commented line (//module xyz)
currently I am using sed command as,
sed -i "s|$var1|$var2|g" file_name
But this command doesn't work. It also replace commented line. How can I only replace the line that isn't commented?
Assuming that you know the pattern is at the start of the line, you can use this:
sed "s|^$var1|$var2|" file_name
That is, add an anchor ^, so that the match has to be at the start of the line.
I removed the -i switch so you can test it and also the g modifier, which isn't necessary as you only want to do one substitution per line.
It's worth mentioning that using shell variables in sed is actually quite tricky to do in a reliable way, so you should take this into account.
Your shell variable assignment should be quoted if there is space. Like:
var1="foo bar blah"
You can add pattern, "the lines don't start with // " to your sed command, so that do your substitution only for those lines
This line should work for your example:
sed -i "\#^//#\!s/$var1/$var2/g" file
the ! needs to be escaped, because we used double quote
since your pattern (comment) has slash (/), I used other char as regex separator
This command will only do substitution on lines not starting with //. If there are leading spaces, you may want to adjust the pattern ^//
You need to identify a pattern so that lines containing that pattern should not be processed.
Assuming that // will exist only in commented lines you can use
sed -i '/\/\// !s/$var1/$var2/g' file_name
/\/\// will enable sed to identify lines which contain the pattern //, and !s will enable you to skip those lines.

Replacing multiple line using sed command

I have a text file file.log contains following text
file.log
ab
cd
ef
I want to replace "ab\ncd" with "ab\n" and the final file.log should look like this:
ab
ef
This is the sed command I am using but it couldn't recognize the newline character to match the pattern:
sed -i 's/\(.*\)\r \(.*\)/\1\r/g' file.log
with 3 character space after '\r' but no change is made with this.
\(.*\) - This matches any character(.) followed by 0 or more (*) of the preceding character
\r - For newline
\1 - Substitution for the first matching pattern. In this case, it's 'ab'
Can you help me out what's wrong with the above command.
The issue is that, the sed is a stream editor, which reads line by line from the input file
So when it reads line
ab
from the input file, it doesnt know whether the line is followed by a line
cd
When it reads the line cd it sed will habe removed the line ab from the pattern space, this making the pattern invalid for the current pattern space.
Solution
A solution can be to read the entire file, and append them into the hold space, and then replace the hold space. As
$ sed -n '1h; 1!H;${g;s/ab\ncd/ab\n/g;p}' input
ab
ef
What it does
1h Copies the first line into the hold space.
1!H All lines excpet the first line (1!) appends the line to the hold space.
$ matches the last line, performs the commands in {..}
g copies the contents of hold space back to pattern space
s/ab\ncd/ab\n/g makes the substitution.
p Prints the entire patterns space.
Sed processes the input file line by line. So can't do like the above . You need to include N, so that it would append the next line into pattern space.
$ sed 'N;s~ab\ncd~ab\n~g' file
ab
ef
A couple of other options:
perl -i -0pe 's/^ab\n\Kcd$//mg' file.log
which will change any such pattern in the file
If there's just one, good ol' ed
ed file.log <<END_SCRIPT
/^ab$/+1 c
.
wq
END_SCRIPT

Resources