manipulating sed context - sed

Here is my one-liner:
sed -n '/BEGIN/,/END/{$d;1d;p}' query
And query:
trash
BEGIN first
labas
END
nieko nėra
BEGIN second
iki
END
nesimato
I expect this result:
labas
iki
However, I get this:
BEGIN first
labas
END
BEGIN second
iki
END
What do I misunderstand about sed context? Shouldn't {$d,1d;p} delete first and last line of the matching input?

No, it deletes any line of the matching input that is the first or last line of the file. You can see the effect if you remove the first two lines of query (so that the first line is "BEGIN").

This might work for you:
sed -n '/BEGIN/,/END/{//!p}' file
labas
iki

Related

SED - using $ inserts string at beginning of line instead of end

I am not getting expected results from sed 's/$/2021-07-21/' demotoytable.csv
Before the command the top 3 lines look like:
urlhm|main_code|description|taxable|itemnum|xtras
t3mr.com/guitar/qrc/G19RTE000000753|G19RTE0000007530|Promo_labor_day_006|Consignment|7522831|bag
t3mr.com/guitar/qrc/G19RTE000000753|G19RTE0000007530|Promo_labor_day_006|Consignment|7522835|box
t3mr.com/guitar/qrc/G19RTE000000753|G19RTE0000007530|Promo_labor_day_006|Consignment|7522839|case
But after running the command sed 's/$/|2021-07-21/' demotoytable.csv
I get this result:
|2021-07-21code|description|taxable|itemnum|xtras
|2021-07-21itar/qrc/G19RTE000000753|G19RTE0000007530|Promo_labor_day_006|Consignment|7522831|bag
|2021-07-21itar/qrc/G19RTE000000753|G19RTE0000007530|Promo_labor_day_006|Consignment|7522835|box
|2021-07-21itar/qrc/G19RTE000000753|G19RTE0000007530|Promo_labor_day_006|Consignment|7522839|case
Any ideas on why this is happening, or better yet how to fix? I want each line to end w "|2021-07-21", not begin with it. On a Mac Pro running Big Sur
Thanks
Remove carriage returns and then add the texts you wish to add:
sed 's/\r$//; s/$/|2021-07-21/' demotoytable.csv
s/\r$// removes carriage returns at the end of lines, s/$/|2021-07-21/ in its turn appends the value of your choice at the end of lines.

Multi-line search with sed

I need to append a few lines to a configuration file. The format is something like follows:
[Topic1]
param=foo
param=bar
param=foobar
[Topic2]
param=one
param=two
etc...
I am trying to write a script using sed to append parameters to a specific topic. Since all the topics have param=, I can't just insert a line after the last occurrence of that string. Also, I can't count on the value of the last parameter being consistent so for example I can't just insert a line after the string param=two
Any help would be appreciated. I'm not too familiar with mutliline sed-fu.
Thanks!
sed -i -r ':a; N; $!ba; s/\[Topic1\]\n(param=[a-zA-Z]*\n)*/&param=VALUE\n/g' FILE_NAME
Basically what :a; N; $!ba; doing is append all line when not the last line (N) to the tag created by :a so that we can use \n in our expression.
Then match [Topic1] followed by arbitrary number of param=xxx, and append param=VALUE to the end of the matching result (&).

Use sed to replace ony one occurrence in a certain block

I want to replace ignore_broadcast_ssid=1 with ignore_broadcast_ssid=0
inside the file /var/run/hostapd-phy0.conf.
This would be my first guess:
sed 's/ignore_broadcast_ssid=1/ignore_broadcast_ssid=0/g' /var/run/hostapd-phy0.conf
But this replaces this option globally, How can I only replace this in one of the sections, starting with bss=wlan0-2 inside the file?
...
bss=wlan0-2
ctrl_interface=/var/run/hostapd
ap_isolate=1
disassoc_low_ack=1
preamble=1
wmm_enabled=1
ignore_broadcast_ssid=0
uapsd_advertisement_enabled=1
auth_algs=1
wpa=0
ssid=temp_wifi
bridge=br-client
bssid=a0:f3:c1:d8:b7:7c
interface=client0
ctrl_interface=/var/run/hostapd
ap_isolate=1
disassoc_low_ack=1
preamble=1
wmm_enabled=1
...
You can first find out the line number of the first text as the starting search index:
grep -n "bss=wlan0-2"
Let assume it is at Line 10. Then apply your sed command at follow:
sed '10s/ignore_broadcast_ssid=1/ignore_broadcast_ssid=0/' /var/run/hostapd-phy0.conf
Make sure you don't have the keyword g at the end of the command as it indicates to replace the matching pattern globally.
sed '/bss=wlan0-2/,/ignore_broadcast_ssid/{s/ignore_broadcast_ssid=1/ignore_broadcast_ssid=0/}' file

sed n doesn't seem to work quite the way I thought it would

I was trying to copy an example I found here : http://www.grymoire.com/Unix/Sed.html#uh-35a
here is the sed pattern
/^begin$/,/^end$/{
/begin/n
/end/!d
}
here's the first file
begin
one
end
last line
and here's the second
begin
end
last line
when I run the sed on the first file it deletes what's between the begin/end and all is well. When I run it on the second, it appears to miss the "end" and deletes the rest of the file.
running on first file
$ sed -f x.sed a
begin
end
last line
running on second
$ sed -f x.sed b
begin
end
notice how "last line" is missing on the second run.
I thought that "n" would print the current pattern and suck in the next one. It would then hit the /end/ command and process that.
as it is, it seems like it's somehow causing the end of the range to be missed. Will somebody explain what is happening?
It should be:
/^begin$/,/^end$/{
/^begin$\|^end$/!d
}
Why was your command wrong?
The n command was wrong there. In the second example it will:
begin ---> n read next line(important: this does not affect the state of the range address (begin,end))
1a. end ---> /end/! does not apply. Don't delete the line
last line ---> /end/! applies. Delete the line. (sed is still searching for a line that contains end because the n command skipped that line)
found another way around after #hek2mgl 's help. I can add a branch around the 2nd statement. I actually need this because I want to see the begin label. so you can also do this:
/^begin$/,/^end$/{
/begin/{ b skip }
/end/!d
:skip
}
I think you were close to getting it to do what you wanted. When you want to delete the next line after a match you simply need to pull it in with the sed n and then hit it with a delete d.
It looks like you want to skip the line after the line that starts with begin unless it's end and print all the other lines.
If so, the following should suffice:
/^begin$/,/^end$/{
/begin/{n;/end/!d}
}
It works by skipping the next line after begin except if it starts with end (/end/!).
Also see: sed or awk: delete n lines following a pattern

How to find patterns across multiple lines using perl

I want to grep some string spread along multiple lines withing some begin and end pattern
Example:
MediaHelper->fetchStrings( names => [ //Here new line may or many not be
**'ubp-firstrun_heading',
'firstrun_text',
'_firstrun-or-start_search',
'installed'** //may end here also );
]);
using perl or grap how I can get list 4 strings here begin pattern is MediaHelper->fetchStrings(names => [ and end pattern is );
Or any other suggesting using other commands like grep or sed or awk ?
Try this:
sed -n '/MediaHelper->fetchStrings( names =>/,/);/ p' <yourfile>
Or, if you want to skip the delimiting lines, this:
sed -n '/MediaHelper->fetchStrings( names =>/,/);/ {/MediaHelper->fetchStrings( names =>/b; /^);/b; p}' <yourfile>
If I understand your question, you need to match all strings in all lines (and not just the MediaHelper thing).
If this is the case, then sed is the right tool, because it is by default line-oriented.
In our case, if you want to match the string in every line:
sed "s/.*\('.*'\).*/\1/" <your_file>
Hope it helps
Edit: To be more descriptive, first we need to match the whole line (that's the first and the last .*) and then we enclose in parenthesis the part of the line we want to print, which in our case is everything inside single quotes. The number 1 before the last delimiter denotes that we want to print the first (in our case it is the last also) parenthesis.
Just process the file in slurp mode instead of line by line:
perl -0777 -ne 'print $1 while m{MediaHelper->fetchStrings(names\s*=>\s*\[(.*?)\]}g' file
Explanation:
Switches:
-0777: Slurp mode instead of line by line
-n: Creates a while(<>){..} loop for each line in your input file.
-e: Tells perl to execute the code on command line.