8

I'm trying to duplicate lines in a text file that contain certain special characters, but in the duplicate, substitute the special character with "regular" ASCII characters. The concrete use-case are accented characters.

Input:

éva
test
frédéric

Desired output:

éva
eva
test
frédéric
frederic

For now I can duplicate the lines containing the é character, but I'm not sure how to search and replace in the capture group.

Here is what I've got so far:

echo 'éva\ntest\nfrédéric' | sed 's/\(.*é.*\)/&\n\1/'

Can I do that with sed? If not, I'll be glad to work with awk...

1
  • What sort of output do you need if a name has two different characters from your special list?? Commented Nov 3, 2021 at 17:40

4 Answers 4

15

You can match on é and then apply multiple commands:

sed '/é/{p;s/é/e/g;}'

For any line containing é, this prints the current pattern space, then replaces all és with e (and prints the pattern space again).

The AWK equivalent is

awk '/é/{print; gsub(/é/, "e")}1'

sed’s s command can re-use the address pattern:

sed '/é/{p;s//e/g;}'

and if your replacements are all single-character replacements, the y command is more efficient:

sed '/é/{p;y/é/e/;}'
5
  • I should have thought about that. Thanks for that ; I was thinking upside down
    – Fred Tep
    Commented Nov 2, 2021 at 13:59
  • I reached to a similar answer but without the brackets, what would be the difference, or there's not any at all? (I think I know, but...) Commented Nov 2, 2021 at 14:13
  • 2
    @schrodigerscatcuriosity the result is the same, in theory there could be a performance difference (your approach matches é in all lines twice). Commented Nov 2, 2021 at 14:16
  • I timed them with 10.000 lines and there's no significant difference, maybe with larger files it really does. Commented Nov 2, 2021 at 14:25
  • 2
    @schrodigerscatcuriosity yes, it’s marginal; profiling both approaches shows ~9.4% more CPU instructions in your variant (but only ~3.6% more cycles), with ~6.4% more branches and ~4.5% more branch misses on Haswell. I used /usr/share/dict/french, a 346,200-line file producing 454,926 lines of output. Commented Nov 2, 2021 at 14:31
11
$ awk '1; gsub(/é/,"e")' file
éva
eva
test
frédéric
frederic

The above uses:

  1. The idiomatic true condition 1 to cause awk to perform the default action of printing the current line, then:
  2. gsub() to replace any és with es and if that found/replaced any és then it's positive return used in the condition context again causes awk to perform the default action of printing the current (now modified) line.

Note that by using the return code from gsub() to tell us if any és were found it saves us from redundantly having to specify the same regexp /é/ twice in the command.

0
6

Yet another sed option - inspired by @EdMorton's awk answer

sed -n 'p;s/é/e/gp' file
5

Another option, similar to @Stephen Kitt's:

$ sed '/é/p;s/é/e/g'
éva
eva
test
frédéric
frederic

  • /é/p select the lines that have an é character and print.
  • s/é/e/g print the previous lines with the substitution.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .