2

A propo of this question.

Let's say I want to substitute <number with commas> for <number> in string something, "10,000", something.

Number could be 1,000, 10,000... so on.

I was experimenting if it's possible to use a command for the substitution:

echo 'something, "10,000", something' | sed "s/\(\".*\"\)/$(echo \\1 | sed 's/,//')/"

Which doesn't work, nothing happens.

What triggers my question is that if I only use echo

echo 'something, "10,000", something' | sed "s/\(\".*\"\)/$(echo \\1)/"

the parameter \\1 is read OK, but it looks like it can't be piped to sed (or grep).

Is this possible in some way?

6
  • 4
    The command substitution in the replacement part of your sed command is executed before the sed command runs.
    – Kusalananda
    Aug 9, 2019 at 17:15
  • .. which means it will sed the string '\1' and not the actual captured string. Aug 9, 2019 at 17:38
  • @JonasBerlin That was my conclusion, what loses me is that echo \\1 outputs the captured pattern, not the string "\1" Aug 9, 2019 at 17:43
  • yeah the \1 gets used in the main sed command replacement part as-is and thus the replacement becomes the captured group's contents. Aug 9, 2019 at 17:45
  • 1
    I'd suggest that you try running your pipelines with set -x activated.
    – Kusalananda
    Aug 9, 2019 at 18:02

2 Answers 2

1

The expansion of the command substitution that is part of your sed expression happens before the sed command is executed.

This means that the shell, to execute the command

sed "s/\(\".*\"\)/$(echo \\1 | sed 's/,//')/"

first runs

echo \\1 | sed 's/,//'

This outputs \1 since there are no commas in the string outputted by echo.

The shell then inserts this string where the command substitution was, yielding

sed "s/\(\".*\"\)/\1/"

This is also clear if we run the pipeline with tracing enabled in the shell:

$ set -x
$ echo 'something, "10,000", something' | sed "s/\(\".*\"\)/$(echo \\1 | sed 's/,//')/"
+ echo 'something, "10,000", something'
+ echo '\1'
+ sed s/,//
+ sed 's/\(".*"\)/\1/'
something, "10,000", something

In your second pipeline, the same thing happens (minus the execution of sed 's/,//'):

$ set -x
$ echo 'something, "10,000", something' | sed "s/\(\".*\"\)/$(echo \\1)/"
+ echo 'something, "10,000", something'
+ echo '\1'
+ sed 's/\(".*"\)/\1/'
something, "10,000", something

In conclusion: It is not possible to call another shell command from within sed using command substitutions to process a substring matched by a regular expression, as the command substitutions are processed before sed is called (in order to resolve what the sed editing script is before calling sed).

sed moreover does not provide any mechanism in its language to call out to another command, like some other editing/processing languages do (e.g. the rudimentary r and w commands in the ed editor allows this, as does awk).

1
  • With GNU sed one could do e.g. echo 'something, "10,000", something' | sed 's/.*"\([[:digit:]]\+\),\?.*/seq \1/e'. The e can be used as a flag for the s function or as a standalone function
    – LL3
    Aug 9, 2019 at 22:24
0
sed 's/\([0-9]\),\([0-9]\)/\1\2/g'

This works only if there are at least two numbers between two commas, so

123,456,789 -> 123456789
12,34,56,78 -> 12345678
but
1,2,3,4,5,6 -> 12,34,56
1
  • sed -e :top -e 's/\([[:digit:]]\),\([[:digit:]]\)/\1\2/g; ttop'
    – Kusalananda
    Aug 9, 2019 at 21:40

You must log in to answer this question.

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