Casper
Casper

Reputation: 1723

Sed command failing because of / in replacement pattern

I have this ECS task definition as follow:

{
    ...
    "image": "123.dkr.ecr.us-east-1.amazonaws.com/foo:1.0",
    ...
    "image": "123.dkr.ecr.us-east-1.amazonaws.com/bar:latest",
    ....
}

I need to replace only the first "image" value, for instance:

{
    ...
    "image": "123.dkr.ecr.us-east-1.amazonaws.com/foo:2.0",
    ...
    "image": "123.dkr.ecr.us-east-1.amazonaws.com/bar:latest",
    ....
} 

Here's my command sed -e "s/.*foo:.*/\"image\":\"${REPO}:${VERSION}\",/" taskdef.json

Where REPO=123.dkr.ecr.us-east-1.amazonaws.com/foo and VERSION=2.0

This is the error I got:

sed: -e expression #1, char 70: unknown option to `s'

This happens because the slash / from REPO variable.

Upvotes: 1

Views: 248

Answers (3)

RomanPerekhrest
RomanPerekhrest

Reputation: 92854

The right way with json processor called jq (v1.5):

Sample ECS task definition task.json:

{
  "containerDefinitions": [
    {
      "name": "wordpress",
      "links": [
        "mysql"
      ],
      "image": "123.dkr.ecr.us-east-1.amazonaws.com/foo:1.0",
      "essential": true,
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80
        }
      ],
      "memory": 500,
      "cpu": 10
    },
    {
      "environment": [
        {
          "name": "MYSQL_ROOT_PASSWORD",
          "value": "password"
        }
      ],
      "name": "mysql",
      "image": "123.dkr.ecr.us-east-1.amazonaws.com/bar:latest",
      "cpu": 10,
      "memory": 500,
      "essential": true
    }
  ],
  "family": "hello_world"
}

The job:

jq '.containerDefinitions[0].image = (.containerDefinitions[0].image | sub("1.0$";"2.0"))' task.json

The output:

{
  "containerDefinitions": [
    {
      "name": "wordpress",
      "links": [
        "mysql"
      ],
      "image": "123.dkr.ecr.us-east-1.amazonaws.com/foo:2.0",
      "essential": true,
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80
        }
      ],
      "memory": 500,
      "cpu": 10
    },
    {
      "environment": [
        {
          "name": "MYSQL_ROOT_PASSWORD",
          "value": "password"
        }
      ],
      "name": "mysql",
      "image": "123.dkr.ecr.us-east-1.amazonaws.com/bar:latest",
      "cpu": 10,
      "memory": 500,
      "essential": true
    }
  ],
  "family": "hello_world"
}

Upvotes: 0

Ed Morton
Ed Morton

Reputation: 203129

To replace the value for the first image would be:

$ awk -v repo="$REPO" -v vers="$VERSION" '
    !f && ($1~/"image"/) { f=1; sub(/:.*/,""); $0=$0 ": \"" repo ":" vers "\"," } 1
' file
{
    ...
    "image": "123.dkr.ecr.us-east-1.amazonaws.com/foo:2.0",
    ...
    "image": "123.dkr.ecr.us-east-1.amazonaws.com/bar:latest",
    ....
}

The above would convert escape sequences to their literal characters (e.g. \t to a literal tab character) if they appeared in REPO or VERSION. It's a trivial workaround if that's a possible issue (just set them on the command line or export them then access with ENVIRON[]) and it'll work no matter what other characters appear in the strings since it's using literal string functionality.

Upvotes: 0

0x00
0x00

Reputation: 307

You can use any character as the delimiter for `s' commands in sed, the first character after s will be the delimiter. For example - #

sed -e "s#foo:.*#\"image\":\"${REPO}:${VERSION}\",#" taskdef.json

Will resolve this particular issue (assuming no # in $REPO or $VERSION) as the / will no longer break the pattern.

Upvotes: 2

Related Questions