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.

  • 1
    How do you determine a paragraph, is it just each line? – Jesse_b Jul 6 '18 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 '18 at 17:17
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
  • 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 '18 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 '18 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 '18 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 '18 at 17:55
  • 1
    @DanMan3395, I've extended my answer to wrap non-blank lines with paragraph tags – Jeff Schaller Jul 6 '18 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>
  • 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 '18 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 '18 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 '18 at 17:22
  • sed '/^$/n;s#^\([^ ]*\)#<i>\1</i><br/>#' input.txt. – slm Jul 6 '18 at 17:25
  • how do i change this to insert the tags around entire lines or paragraphs? (^ $]*) ? – DanMan3395 Jul 6 '18 at 18:13
1

You can try with awk:

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

  • Isn't this missing quotes? Should be "<i>" $1 "</i>", no? – Benjamin W. Apr 23 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.