How to concatenate two lines using label in sed - sed

I have a question if there is some way to detect the end of the line using sed. Bcs I need to concatenate two-line only if the end of the line end with a minus sign - otherwise not.
sed -e :a -e '/,$/N; s/-\n*/new_line/; ta' test.txt (that is only what i have and i need to substite new_line for actualy new line)
if file is look something like that
Here is a random sente-
nc and if random sentec ended with minus it is better to concatenate.
RESULT
Here is a random sentece and if random sentec ended with minus it is better to concatenate.

This might work for you (GNU sed):
sed '/-$/{N;s/-\n//}' file
If the line ends in -, append the next line and remove the last character of the first line and the following newline.

Here is how you will do that sed in any version:
cat file
foo bar_-
Here is a random sente-
nce and if random sentec ended with minus it is better to concatenate.
# and use sed as
sed -e :a -e '/-$/{N;s/-\n//g;ta' -e '}' file
foo bar_Here is a random sentence and if random sentec ended with minus it is better to concatenate.

Related

Select line matching pattern +1

How do I use sed to select every line matching a pattern + the next line?
For instance, I'd like to select all lines with tag="foo" plus the next line.
As an alternative, I'd also like to be able to select lines with tag="foo" OR group="bar" plus the next line.
This might work for you (GNU sed):
sed -En '/tag="foo"|group="bar"/,+1p' file
Turn on extended regexp -E and off implicit printing -n.
Match the alternation of tag="foo" or group="bar" and print the range +1 line(s).
Alternative:
sed '/tag="foo"\|group="bar"/!d;n' file
To always print 2 lines, use:
sed -n '/tag="foo"\|group="bar"/{N;p}' file

How to change the first occurrence of a line containing a pattern?

I need to find the line with first occurrence of a pattern, then I need to replace the whole line with a completely new one.
I found this command that replaces the first occurrence of a pattern, but not the whole line:
sed -e "0,/something/ s//other-thing/" <in.txt >out.txt
If in.txt is
one two three
four something
five six
something seven
As a result I get in out.txt:
one two three
four other-thing
five six
something seven
However, when I try to modify this code to replace the whole line, as follows:
sed -e "0,/something/ c\COMPLETE NEW LINE" <in.txt >out.txt
This is what I get in out.txt:
COMPLETE NEW LINE
five six
something seven
Do you have any idea why the first line is lost?
The c\ command deletes all lines between and inclusive the first matching address through the second matching address, when used with 2 addresses, and prints out the text specified following the c\ upon matching the second address. If there is no line matching the second address in the input, it just deletes all lines (inclusively) between the first matching address through the last line. Since you want to replace one line only, you shouldn't use the c\ command on an address range. The c\ is immediately followed by a new-line character in normal usage.
The 0,/regexp/ address range is a GNU sed extension, which will try to match regexp in the first input line too, which is different from 1,/regexp/ in that aspect. So, the correct command in GNU sed could be
sed '0,/something/{/something/c\
COMPLETE NEW LINE
}' < in.txt
or simplified as pointed out by Sundeep
sed '0,/something/{//c\
COMPLETE NEW LINE
}' < in.txt
or a one-liner,
sed -e '0,/something/{//cCOMPLETE NEW LINE' -e '}' < in.txt
if a literal new-line character is not desirable.
This one-liner also works as pointed out by potong:
sed '0,/something/!b;//cCOMPLETE NEW LINE' in.txt
This might work for you (GNU sed):
sed '1!b;:a;/something/!{n;ba};cCOMPLETE NEW LINE' file
Set up a loop that will only operate from the first line.
Within in the loop, if the key word is not found in the current line, print the current line, fetch the next and repeat until the end of the file or a match is found.
When a match is found, change the contents of the current line to the required result.
N.B. The c command terminates any further processing of sed commands in the same way the d command does.
If there are lines in the input following the key word match, the negation of address at the start of the sed cycle will capture these lines and result in their printing and no further processing.
An alternative:
sed 'x;/./{x;b};x;/something/h;//cCOMPLETE NEW LINE' file
Or (specific to GNU and bash):
sed $'0,/something/{//cCOMPLETE NEW LINE\n}' file
Just use awk:
$ awk '!done && sub(/something/,"other-thing"){done=1} {print}' file
one two three
four other-thing
five six
something seven
$ awk '!done && sub(/.*something.*/,"other-thing"){done=1} {print}' file
one two three
other-thing
five six
something seven
$ awk '!done && /something/{$0="other-thing"; done=1} {print}' file
one two three
other-thing
five six
something seven
and look what you can trivially do if you want to replace the Nth occurrence of something:
$ awk -v n=1 '/something/ && (++cnt == n){$0="other-thing"} {print}' file
one two three
other-thing
five six
something seven
$ awk -v n=2 '/something/ && (++cnt == n){$0="other-thing"} {print}' file
one two three
four something
five six
other-thing

Get all numbers in a file using a line break as separator

I have a file containing many blocks of lines. In each block, I have one numeric character of multiple digits (15353580 for instance). I need to extract all these numbers and put them as a column in a new file.
I came across this thread. The sed command does the job but does not separate the numbers from each other. Using the second example ("123 he23llo") of the most voted response, I would like to have 123-23 instead of 12323, where my '-' stands for a line break. How can I do so ?
You can use this sed,
sed -e 's/[^0-9]\+/-/g' -e 's/-$//'
Example:
$ echo "123 hel43lo 23fds" | sed -e 's/[^0-9]\+/-/g' -e 's/-$//'
123-43-23

sed command to substitute any content after equals on line 3 with a string

I am trying to substitute this line
<data_item name="any_text">
which is on line 3 with
<data_item name="my_text">
So I tried something like sed '3s/=*/=my_text/' input_file > output_file
But this is printing my text at the beginning of the line. Tried braces around (=*) and (=my_text) but that doesn't do anything.
Try this
sed '3s/any_text/my_text/' file
Example:
$ echo '<data_item name="any_text">' | sed '1s/any_text/my_text/'
<data_item name="my_text">
Your code:
sed '3s/=*/=my_text/'
Your code will replace 0 or more equals on the third line with =my_text.
sed '3s/=".*"/="mytext"/'
should work. You need the . because it matches any character and the * says you want 0 or more of the preceding symbol .. In regex terms the * doesn't mean match anything. It is a quantifier which means it specifies and amount and is generally used to specify the number of the preceding terms you want to match.
sed '3 c\
<data_item name="my_text">' YourFile
Assuming that the content of my_text does not contain unescape \ & and '
avoid < and > on same file in samle instruction, could have unexpected result. Use a temporary file on -i on GNU sed

sed: replace pattern only if followed by empty line

I need to replace a pattern in a file, only if it is followed by an empty line. Suppose I have following file:
test
test
test
...
the following command would replace all occurrences of test with xxx
cat file | sed 's/test/xxx/g'
but I need to only replace test if next line is empty. I have tried matching a hex code, but that doesn ot work:
cat file | sed 's/test\x0a/xxx/g'
The desired output should look like this:
test
xxx
xxx
...
Suggested solutions for sed, perl and awk:
sed
sed -rn '1h;1!H;${g;s/test([^\n]*\n\n)/xxx\1/g;p;}' file
I got the idea from sed multiline search and replace. Basically slurp the entire file into sed's hold space and do global replacement on the whole chunk at once.
perl
$ perl -00 -pe 's/test(?=[^\n]*\n\n)$/xxx/m' file
-00 triggers paragraph mode which makes perl read chunks separated by one or several empty lines (just what OP is looking for). Positive look ahead (?=) to anchor substitution to the last line of the chunk.
Caveat: -00 will squash multiple empty lines into single empty lines.
awk
$ awk 'NR==1 {l=$0; next}
/^$/ {gsub(/test/,"xxx", l)}
{print l; l=$0}
END {print l}' file
Basically store previous line in l, substitute pattern in l if current line is empty. Print l. Finally print the very last line.
Output in all three cases
test
xxx
xxx
...
This might work for you (GNU sed):
sed -r '$!N;s/test(\n\s*)$/xxx\1/;P;D' file
Keep a window of 2 lines throughout the length of the file and if the second line is empty and the first line contains the pattern then make a substitution.
Using sed
sed -r ':a;$!{N;ba};s/test([^\n]*\n(\n|$))/xxx\1/g'
explanation
:a # set label a
$ !{ # if not end of file
N # Add a newline to the pattern space, then append the next line of input to the pattern space
b a # Unconditionally branch to label. The label may be omitted, in which case the next cycle is started.
}
# simply, above command :a;$!{N;ba} is used to read the whole file into pattern.
s/test([^\n]*\n(\n|$))/xxx\1/g # replace the key word if next line is empty (\n\n) or end of line ($)