Sed: Difference between revisions

From Leo's Notes
This page was last edited on 30 December 2021, at 21:21.
m Text replacement - "Category:Linux{{Navbox Linux}}" to "{{Navbox Linux}}Category:Linux"
m Leo moved page Sed Tips & Tricks to Sed: Generic information on sed
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Tasks ==


===Simple search / replace===
Global search and replace:
{{highlight|lang=terminal|code=
$ sed 's/<search>/<replace>/g'
}}


== Modifying a File Directly ==
To replace only the first match:
{{highlight|lang=terminal|code=
$ sed '0,/<search>/s//<replace>/'
}}


Using the above tips, you can modify a file directly by using the {{code|-i}} parameter, with the filename as the second argument:
==== Modifying a file directly ====
sed -i "search" /path/to/file
The search and replace above only does things inline but doesn't modify the file. In order to operate on a file directly, use the <code>-i filename</code> option.
{{highlight|lang=terminal|code=
$ sed -i "s/search/replace/g" /path/to/file
}}


== Simple search / replace ==
You can also glob multiple files.
Global search and replace:
{{highlight|lang=terminal|code=
sed 's/<search>/<replace>/g'
$ sed -i -e "s/search/replace/g" files*.txt
}}


To replace only the first match:
====Recursively search and replace====
sed '0,/<search>/s//<replace>/'
<code>$ grep -lRZ 'search' . | xargs -0 -l sed -i -e 's/search/replace/g'</code>


== Remove HTML tags ==
===Remove HTML tags===


sed -e 's/<[^>]*>//g' <file>
{{highlight|lang=terminal|code=
$ sed -e 's/<[^>]*>//g' <file>
}}


== Remove File Extensions ==
===Remove File Extensions===


sed 's/\(.*\)\..*/\1/'
{{highlight|lang=terminal|code=
$ sed 's/\(.*\)\..*/\1/'
}}


eg:
eg:
echo "house.txt" | sed 's/\(.*\)\..*/\1/'  # returns 'house'
{{highlight|lang=terminal|code=
$ echo "house.txt" {{!}} sed 's/\(.*\)\..*/\1/'  # returns 'house'
}}


== Delete Matching Line ==
===Delete Matching Line===


sed '/someline/d'
{{highlight|lang=terminal|code=
$ sed '/someline/d'
}}


== Inserting a Line Before/After a Match ==
===Inserting a Line Before/After a Match===


Use: {{code|0,/<regex>/s//<replacement text where \0 is the found line>/}}
Use: {{code|0,/<regex>/s//<replacement text where \0 is the found line>/}}
# Add a password line before the first title entry in the grub.conf file.
{{highlight|lang=terminal|code=
sed -i "0,/^title/s//password --encrypted $Password\n\n\0/" $GrubConfig
## Add a password line before the first title entry in the grub.conf file.
$ sed -i "0,/^title/s//password --encrypted $Password\n\n\0/" $GrubConfig
}}


To append after, just put \0 before your replacement text.
To append after, just put \0 before your replacement text.
Line 55: Line 78:
}}
}}


== Grap Specific Section using Regex ==
===Grab Specific Section using Regex===
To grab a specific section of a string, for instance '3' in 'Server time offset: 3':
To grab a specific section of a string, for instance '3' in 'Server time offset: 3':
echo 'Server time offset: 3' | sed -n -e 's/.*: \([0-9]*\)/\1/p'
 
{{highlight|lang=terminal|code=
$ echo 'Server time offset: 3' {{!}} sed -n -e 's/.*: \([0-9]*\)/\1/p'
}}


This can also be done with grep, though it isn't regex:
This can also be done with grep, though it isn't regex:
echo 'Server time offset: 3' | /bin/grep -ohE '[0-9]*'
{{highlight|lang=terminal|code=
$ echo 'Server time offset: 3' {{!}} /bin/grep -ohE '[0-9]*'
}}


== Join Every Other Line ==
===Join Every Other Line===
To join every other line, useful for making key:value pairs, use:
To join every other line, useful for making key:value pairs, use:
{{highlight|lang=terminal|code=
{{highlight|lang=terminal|code=
Line 68: Line 96:
}}
}}


===Removing Newlines===
If the output you have is wrapped with newlines indented in by a known amount, such as:
{{Highlight
| code = Fri Jul 24 19:21:55: Dispatched 1 Task(s) on Host(s) <node008>, Allocated 1 Slo
                    t(s) on Host(s) <node008>, Effective RES_REQ <select[type
                    == local] order[r15s:pg] >;
| lang = text
}}
You can use sed to replace a newline followed by a specific number of spaces to make this one whole line again:
{{Highlight
| code = # cat above {{!}} sed ':a;N;$!ba;s/\n                    //g'
Fri Jul 24 19:21:55: Dispatched 1 Task(s) on Host(s) <node008>, Allocated 1 Slot(s) on Host(s) <node008>, Effective RES_REQ <select[type == local] order[r15s:pg] >;
| lang = terminal
}}My understanding of how this works is:
*<code>:a</code> creates a new label 'a'
*<code>N</code> to join next line to the current line
*<code>$!</code> if the last command failed (no more new lines?), branch to label a
*Otherwise, with the next line joined, we search and replace (like usual) by searcing for <code>\n</code>    and replacing it with nothing.
===Place a key=value pair in its own line===
{{Highlight
| code = sed -r 's/[[:alnum:]]+=/\n&/g'
| lang = text
}}
==Further Reading==


== Further Reading ==
*http://www.catonmat.net/blog/sed-one-liners-explained-part-one/
* http://www.catonmat.net/blog/sed-one-liners-explained-part-one/


{{Navbox Linux}}[[Category:Linux]]
{{Navbox Linux}}
[[Category:Linux]]
[[Category:TipsnTricks]]
[[Category:TipsnTricks]]
[[Category:Code]]
[[Category:Code]]
[[Category:LinuxUtilities]]
[[Category:LinuxUtilities]]

Latest revision as of 21:21, 30 December 2021

Tasks

Simple search / replace

Global search and replace:

$ sed 's/<search>/<replace>/g'

To replace only the first match:

$ sed '0,/<search>/s//<replace>/'

Modifying a file directly

The search and replace above only does things inline but doesn't modify the file. In order to operate on a file directly, use the -i filename option.

$ sed -i "s/search/replace/g" /path/to/file

You can also glob multiple files.

$ sed -i -e "s/search/replace/g" files*.txt

Recursively search and replace

$ grep -lRZ 'search' . | xargs -0 -l sed -i -e 's/search/replace/g'

Remove HTML tags

$ sed -e 's/<[^>]*>//g' <file>

Remove File Extensions

$ sed 's/\(.*\)\..*/\1/'

eg:

$ echo "house.txt" | sed 's/\(.*\)\..*/\1/'  # returns 'house'

Delete Matching Line

$ sed '/someline/d'

Inserting a Line Before/After a Match

Use: 0,/<regex>/s//<replacement text where \0 is the found line>/

## Add a password line before the first title entry in the grub.conf file.
$ sed -i "0,/^title/s//password --encrypted $Password\n\n\0/" $GrubConfig

To append after, just put \0 before your replacement text.

Another example: To add After=network-online.target in a systemd service file:

...
# lightdm takes responsibility for stopping plymouth, so if it fails
# for any reason, make sure plymouth still stops
OnFailure=plymouth-quit.service

[Service]
ExecStart=/usr/sbin/lightdm
Restart=always
...

Running this will append an 'After' dependency to the service file before the service section.

terminal
# sed -i "0,/^\[Service\]/s//After=network-online.target\n\n\0/" /usr/lib/systemd/system/lightdm.service

Grab Specific Section using Regex

To grab a specific section of a string, for instance '3' in 'Server time offset: 3':

$ echo 'Server time offset: 3' | sed -n -e 's/.*: \([0-9]*\)/\1/p'

This can also be done with grep, though it isn't regex:

$ echo 'Server time offset: 3' | /bin/grep -ohE '[0-9]*'

Join Every Other Line

To join every other line, useful for making key:value pairs, use:

'$!N;s/\n/ /'

Removing Newlines

If the output you have is wrapped with newlines indented in by a known amount, such as:

Fri Jul 24 19:21:55: Dispatched 1 Task(s) on Host(s) <node008>, Allocated 1 Slo
                     t(s) on Host(s) <node008>, Effective RES_REQ <select[type
                     == local] order[r15s:pg] >;

You can use sed to replace a newline followed by a specific number of spaces to make this one whole line again:

# cat above | sed ':a;N;$!ba;s/\n                     //g' 
Fri Jul 24 19:21:55: Dispatched 1 Task(s) on Host(s) <node008>, Allocated 1 Slot(s) on Host(s) <node008>, Effective RES_REQ <select[type == local] order[r15s:pg] >;

My understanding of how this works is:

  • :a creates a new label 'a'
  • N to join next line to the current line
  • $! if the last command failed (no more new lines?), branch to label a
  • Otherwise, with the next line joined, we search and replace (like usual) by searcing for \n and replacing it with nothing.

Place a key=value pair in its own line

sed -r 's/[[:alnum:]]+=/\n&/g'

Further Reading