Subject: bug#28056: Substitution doing global when it should not.

From: Assaf Gordon <[email-masked]>
Date: Sat, 12 Aug 2017 19:51:57 -0600

Hello,

On 12/08/17 12:02 PM, Techwolf Lupindo wrote:
> A followup. I tried the command you hinted at. The result was the first
> match and substitution was done, but the rest of the file was deleted. This
> is using sed -i -e.

If you want to operate on just the first matching line, you can use
the following sed method:

  printf "%s\n" a b c d a b c d | sed '/b/ { s/b/X/ ; :Y ; n ; bY }'

The sed program does the following:

 /b/  - match *any* line that contains 'b'

 { ... } - sed commands grouped together, will be executed
          when the current line matches 'b'

 s/b/X/ - replace the first occurrence of 'b' with 'X' .

 :Y     - a label called 'Y' (arbitrary name)

 n      - read the next line and print it

 bY     - jump (=branch) to label Y

Effectively, the first time a line contains 'b'
matches the regex, sed will perform one replacement,
and then enter an endless loop of reading/printing the lines,
without executing any other sed commands (and thus,
not matching other 'b' lines and not performing replacements).

Expanding Eric's suggestion, your sed command might look like:

    sed -e '/COMMAND ${MERCURIAL}/ { s::COMMAND ${MERCURIAL} -- cwd
${CMAKE_SOURCE_DIR}: ; :Y ; n; bY }'

Also,
When experimenting with sed commands, I'd recommend not using "-i"
(in-place replacements) - this could lead to data loss if your sed
command isn't working as expected, and you don't have a backup.

Lastly,
For quick-and-dirty manual sed replacements of single lines, you can
find the exact line number and replace just it.
Example:

  $ wget https://hg....file/9d58c58cca90/indra/cmake/BuildVersion.cmake

  $ grep -n 'COMMAND ${MERCURIAL}' BuildVersion.cmake
  28:         #COMMAND ${MERCURIAL} log -r tip:0 --template '\\n'
  31:         COMMAND ${MERCURIAL} identify -n

And then assuming you want to change only line 28, use a specific
and even shorter sed command:

  sed '28s/}/} --cwd ${CMAKE_SOURCE_DIR} /' BuildVersion.cmake

The '28s/.../.../' tells sed to perform the 's' command only on line 28.

Hope this helps,

regards,
 - assaf