4

So obviously I am trying italicize the first word of each paragraph in a huge document. I think its relatively easy to add the prefix but how do I delineate just the first word for the postfix instead of the end of the line? The file should be space delimited.

sed -e 's/^/<i>/' file > file.new

Do I need to run sed twice with different syntax or is this doable with one command? I am outputting to a new file in case I screw up.

Here is an example of a couple lines of the file:

Snapdragon  Plant with a two-lipped flower.

Snap-fastener  = *press-stud.

Snapper  Any of several edible marine fish.

Snappish  1 curt; ill-tempered; sharp. 2 inclined to snap.

I want it to be as below:

<i>Snapdragon</i>  Plant with a two-lipped flower.

<i>Snap-fastener</i>  = *press-stud.

<i>Snapper</i>  Any of several edible marine fish.

<i>Snappish</i>  1 curt; ill-tempered; sharp. 2 inclined to snap.

not all the lines are single, some terms have multiple lines of definition.

2
  • 1
    How do you determine a paragraph, is it just each line?
    – jesse_b
    Jul 6, 2018 at 15:53
  • looking in vim i don't see line ending characters so i would assume that the entire paragraph is a huge long line.
    – DanMan3395
    Jul 6, 2018 at 17:17

4 Answers 4

4

Using sed,

  • if there's a letter at the beginning of the line, then
  • capture any amount of non-whitespace characters and
  • replace those captured characters with surrounding <i> ... </i>.

like this:

sed '/^[a-zA-Z]/ s!\([^ ]*\)!<i>\1</i>!' < file > file.new

On this sample input:

Snapdragon  Plant with a two-lipped flower.

Snap-fastener  = *press-stud.

Snapper  Any of several edible marine fish.

Snappish  1 curt; ill-tempered; sharp. 2 inclined to snap.

The output is:

<i>Snapdragon</i>  Plant with a two-lipped flower.

<i>Snap-fastener</i>  = *press-stud.

<i>Snapper</i>  Any of several edible marine fish.

<i>Snappish</i>  1 curt; ill-tempered; sharp. 2 inclined to snap.

To break down the pieces of the sed command:

  • /^[a-zA-Z]/ -- this is an address filter; it means to apply the subsequent command only to lines that match this regular expression. The regular expression requires that a letter (either lower-case a-z or upper-case A-Z) must follow the beginning of the line ^.

  • s!\([^ ]*\)!<i>\1</i>! -- this is the search and replace command. It uses a delimiter between the search and the replacement; the common delimiter is a forward-slash, but since the replacement text has a forward-slash, I changed the delimiter to an exclamation point !. The search term has two pieces to it: the capturing parenthesis, which have to be escaped, and the regular expression [^ ]*, which says: "match anything-except-a-space, zero or more times *. The replacement text refers back to that captured group with \1 and surrounds it with the HTML tag.

To additionally wrap each non-empty line with paragraph tags, add another sed expression:

sed -e '/^[a-zA-Z]/ s!\([^ ]*\)!<i>\1</i>!' -e '/./ { s/^/<p>/; s!$!</p>! }' < file

The additional expression says:

  • match lines that have one (any) character -- this skips blank lines
  • { group the next two commands together
  • search and replace the beginning of line ^ with an opening paragraph tag
  • search and replace the end of line $ with a closing paragraph tag
  • } end the grouping
8
  • can you cut that into segments that explain what they do? does ^ represent blank lines or spaces? sed is a real mind bender.
    – DanMan3395
    Jul 6, 2018 at 17:14
  • 1
    sed can be a real mind-bender; I'm a sed amateur; there are some really complex sed scripts around! I've added a breakdown of the sed script; let me know if there's any remaining mystery.
    – Jeff Schaller
    Jul 6, 2018 at 17:20
  • so if i now wanted to write an expression that encapsulates each line with <p> </p> respectively, would it be like this? sed '/^[a-zA-Z]/ s!([^a-Z]*)!<p>\1</p>!' < file > file.new
    – DanMan3395
    Jul 6, 2018 at 17:45
  • 1
    to be particular, your paragraph example would only wrap paragraph tags around lines that start with a letter, not around each line. the \1 is a reference to the capturing group -- the stuff inside the first escaped parenthesis \( ... \)
    – Jeff Schaller
    Jul 6, 2018 at 17:55
  • 1
    @DanMan3395, I've extended my answer to wrap non-blank lines with paragraph tags
    – Jeff Schaller
    Jul 6, 2018 at 18:15
2

You can do this with sed:

$ sed '/^$/n;s#^\([^ ]*\)#<i>\1</i>#' input.txt
<i>Snapdragon</i>  Plant with a two-lipped flower.

<i>Snap-fastener</i>  = *press-stud.

<i>Snapper</i>  Any of several edible marine fish.

<i>Snappish</i>  1 curt; ill-tempered; sharp. 2 inclined to snap.

Explanation

The sed above includes 2 blocks. The first block detects any blank lines, /^$/ and skips them, n.

  • skip any blank lines /^$/n

The second block does all the heavy lifting s#..#..#, and detects sub-strings that do not include a space \([^ ]*\). This pattern is 'saved' via the \(..\) that wraps it, so we can reuse it later on via the \1.

  • match sub-string up to first space \([^ ]*\)
  • save match, \1, and wrap it with <i>...</i>
6
  • sed '/^$/n;s , #^([^ ]*)# , <i>\1</i># , g' - am i parsing this correctly? I am trying to interpret this correctly so i can do more than just this one task with it. I used a comma surrounded by spaces to split up what i see as each command syntax block.
    – DanMan3395
    Jul 6, 2018 at 17:12
  • @DanMan3395 - there's 2 blocks - /^$/n & s#^\([^ ]*\)#<i>\1</i>#. The first block skips blank lines, the 2nd block does all the work of finding matches and wrapping them with the <i>..</i>.
    – slm
    Jul 6, 2018 at 17:19
  • so if i now wanted to insert <br> on all blank lines, would it be as follows? ------- sed '^$;s#^([^ ]*)#<br>#'
    – DanMan3395
    Jul 6, 2018 at 17:22
  • sed '/^$/n;s#^\([^ ]*\)#<i>\1</i><br/>#' input.txt.
    – slm
    Jul 6, 2018 at 17:25
  • how do i change this to insert the tags around entire lines or paragraphs? (^ $]*) ?
    – DanMan3395
    Jul 6, 2018 at 18:13
1

You can try with awk:

awk '{$1="<i>$1</i>"; print $0}' file > file.new

1
  • Isn't this missing quotes? Should be "<i>" $1 "</i>", no? Apr 23, 2019 at 15:55
0

sed extended regular expression

Put <i> and </i> tags around the first substring of [^[:space:]] (non-space) characters, using & to represent the search term in the replacement pattern, whether or not the line is indented.

Using -E to enable sed's extended regular expressions:

sed -E 's/[^[:space:]]+/<i>&<\/i>/' file

When using / to separate the search and replacement terms, you need to precede other / with \ (as in the second tag here). You can avoid this extra step by using a character other than / to separate the search and replacement terms, as long as that character does not appear in the terms. For example, using commas:

sed -E 's,[^[:space:]]+,<i>&</i>,' file

That's the shortest way.

The + (which stands for one or more occurrences of the pattern) doesn't work in ordinary (-e instead of -E) regular expressions, but you can do the same thing using * (which stands for zero or more occurrences) with a little more typing:

sed -e 's,[^[:space:]][^[:space:]]*,<i>&</i>,' file

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.