I have multiple lines in a file like these
APPLE JUICE
APPLE JAM
APPLE JELLY
I want to replace "APPLE" with "ORANGE" and append "SHOP" to the end of the string. The output would be
ORANGE JUICE SHOP
ORANGE JAM SHOP
ORANGE JELLY SHOP
How to do this in sed or vim?
EDIT1:
I found a solution that works in sed
#replace APPLE with ORANGE
sed -i s/APPLE/ORANGE/g foo.txt
#in a line containing ORANGE replace newline with SHOP
sed -i '/ORANGE/s/$/ SHOP/g'
the problem now is that I can't get the second command to work in vim. So this is a vim question now.
No, #Kent's answer elide rows not containing APPLE and append SHOP to all lines in file. You need to use RE sub-expressions:
echo "APPLE JUICE\
CHERRY JAM\
APPLE JELLY" | sed 's/^APPLE \(.*\)$/ORANGE \1 SHOP/'
ORANGE JUICE SHOP
CHERRY JAM
ORANGE JELLY SHOP
In vim itself try this one in command mode
:%s/APPLE \ (.*\ )/ORANGE \1 SHOP/g
here you go:
kent$ echo "APPLE JUICE
APPLE JAM
APPLE JELLY"|sed 's/\bAPPLE\b/ORANGE/;s/$/ SHOP/'
ORANGE JUICE SHOP
ORANGE JAM SHOP
ORANGE JELLY SHOP
I added word boundary, so that something like PINEAPPLE won't be replaced by PINEORANGE
in vim, with the same idea:
%s/\<APPLE\>/ORANGE/|%s/$/ SHOP/
EDIT
when I posted the answer, there is no requirement that, appending SHOP only if there is an ORANGE match. OP updated the question after my answer.
anyway, I added another vim cmd:
%s/\<APPLE\>/ORANGE/g|%s/.*\<ORANGE\>.*/& SHOP/
#dyomas is right that #Kent's answer indiscriminately appends SHOP to all lines.
Here is a solution that does not use sub-expressions; instead, it uses the t command that tests for substitutions and only then appends SHOP.
sed 's/\bAPPLE\b/ORANGE/; t append; b; :append s/$/ SHOP/'
Because the t command jumps in case of substitution (but we need the opposite), we have to jump "over" a b command that otherwise aborts the line processing.
Related
I am looking for a solution that would allow me to search text files on a linux server that would look a file and find a pattern such as:
Text 123
Blue Green
And then replaces it with one line, every time it finds it in a file...
Order Blue Green
I am not sure what would be the easiest way to solve this. I have seen many guides using SED but only for finding one line and replacing it.
You ask about sed, here is an answer in sed.
Let me mention however, that while sed is fun for this kind of exercise, you probably should choose something else, more flexible and easier to learn; perl for example.
look for first line /Text 123/
when found start a loop :a
concat next line N
replace twins of searched text with single copy and print it
s/Text 123\nText 123/Text 123/p;
loop while that replaces ta;
try to replace s///
rely on concat being printed unchanged if replace does not trigger
Code:
sed "/Text 123/{:a;N;s/Text 123\nText 123/Text 123/p;ta;s/Text 123\nBlue Green/Order Blue Green/}"
Test input:
Text 123
Do not replace
Lala
Text 123
Blue Green
lulu
Text 123
Do not replace either
Text 123
Text 123
Blue Green
preceding should be replaced
Output:
Text 123
Do not replace
Lala
Order Blue Green
lulu
Text 123
Do not replace either
Text 123
Order Blue Green
preceding should be replaced
Platform: Windows and GNU sed version 4.2.1
Note:
On that platform the sed line allows to use the environment variables for the two text fragments, which you probably want to do:
sed "/%EnvVar2%/{:a;N;s/%EnvVar2%\n%EnvVar2%/%EnvVar2%/p;ta;s/%EnvVar2%\n%EnvVar%/Order %EnvVar%/}"
Platform2:
still Windows
using bash GNU bash, version 3.1.17(1)-release (i686-pc-msys)
GNU sed version 4.2.1 (same)
On this platform, variables can e.g. be used like:
sed "/${EnvVar2}/{:a;N;s/${EnvVar2}\n${EnvVar2}/${EnvVar2}/p;ta;s/${EnvVar2}\n${EnvVar}/Order ${EnvVar}/}"
On this platform it is important to use "..." in order to be able to use variables,
it does not work with '...'.
As #edMorton has hinted, on all platforms be careful however with trying to replace (using variables) text which looks like using a variable. E.g. with "Text $123" in bash. In that case, not using variables but trying to replace text which looks like variables, using '...' instead of "..." is the way to go.
sed is for simple substitutions on individual lines, that is all. If you find yourself trying to use constructs other than s, g, and p (with -n) then you are on the wrong track as all other sed constructs became obsolete in the mid-1970s when awk was invented.
Your problem is not doing substitutions on individual lines, it's on a multi-line record and to do that with GNU awk for multi-char RS is:
$ awk -v RS='^$' -v ORS= '{gsub(/Text 123\nBlue Green/,"Order Blue Green")}1' file
Order Blue Green
but there are several other approaches depending on your real needs.
How would you remove a string that repeats in most lines of a list with bash?
E.G.
My list looks like this:
Rex Rocket Steam Game
Magdalena Steam Game
FLASHOUT 2 Steam Game
Falcon
Girls Like Robots Steam Game
The Land Of Lamia Steam Game
Aeon Command
And I want to remove all the "Steam Game" string from all the lines that end that way.
I'm super rusty, this looks so easy but I can't figure it out.
There's many options, sed is probably the simplest.
$ sed 's/Steam Game$//' foo.txt
Rex Rocket
Magdalena
FLASHOUT 2
Falcon
Girls Like Robots
The Land Of Lamia
Aeon Command
You asked for "Steam Game" to be removed not " Steam Game". If you want the space also removed, add a space to the regex: 's/ Steam Game$//'. Or use 's/ *Steam Game$//' if there are more than one space.
I need to grep a large file for words (really a string of characters) in a specified order. I also need the string to be able to contain a colon ":". For example, if the words are "apple", "banana", and ":peach", I will get the line that says, "apple cherries banana cool :peach" but not "apple :peach cherries banana cool". I would really like to be able to have one string and not grep commands in other grep commands. I am not concerned about searching whole words only.
grep "apple.*banana.*:peach" file
e.g.
$ echo "apple cherries banana cool :peach" | grep "apple.*banana.*:peach"
apple cherries banana cool :peach
$ echo "apple :peach cherries banana cool" |grep "apple.*banana.*:peach"
$
Pretty simple regex. the "apple", "banana" and ":peach" portions are just literal strings. The .* in between them is are two regex operators - the . will match any character, and the * says that the previous match can match 0 or more times.
In essence we're saying find these literal strings in this order, with any number of characters between them (including none, so even "applebanana:peach" would match.)
I have a text file like this...
apples
berries
berries
cherries
and I want it to look like this...
apples
berries
cherries
That's it. I just want to eliminate doubled entries. I would prefer for this to be an awk or sed "one-liner" but if there's some other common bash tool that I have overlooked that would be fine.
sort -u file
if in case you are not worried about the order of the output.
Remove duplicates by retaining the order:
awk '!a[$1]++' file
There is a special command for this task, called uniq:
$ uniq file
apples
berries
cherries
This requires that common lines are adjacent, not adjacent equal lines are not removed.
I have two text files, ones is the file I'm currently working, the other is kind of dictionary. What I would like to do is to search the first one using the latter as input. I'd like to highlight all matching lines.
E.g.:
File1:
I like eggs
I like meat
I don't like eggplant
My mom likes chocolate
I like chocolate too
File2:
meat
chocolate
In this example the those lines should be highlighted:
I like meat
My mom likes chocolate
I like chocolate too
The approach is similar to the answer I've given to your related question: Use readfile(), join() the lines to a pattern, then use :match.
Here's how I would build this interactively on the command-line:
:match Search /.*\%(<C-R>=join(readfile('file2'), '\|')<CR>\).*/<CR>
Note that this quick one-liner omits error handling, regular expression escaping, etc. In case you need to do this often, I'd suggest you turn this into a custom command, just like I have shown you in the linked answer.