0

I have 3 files: en.json, en 1.json and en 2.json. All of them have the below content

    {
      "ABC" : "/long_name",
      "DFG" : "/{long_name}"
    }

I run below command to replace ABC with 123 and DFG with 456

grep -El 'ABC|DFG' *.json | xargs  sed -ibak -e 's#ABC#123#g' -e 's#DFG#456#g'

text gets replaced in en.json but for en 1.json and en 2.json it fails with the below error

sed: en: No such file or directory

For some reason, the space is ignored and I am not sure how I can handle it within the command

4
  • 1
    Try running grep with -Z/--null to use a null byte as delimiter, and pass -0/--null to xargs aswell Commented Jul 13, 2023 at 14:57
  • not sure i understood it Commented Jul 13, 2023 at 15:01
  • @AdityaNaik, Panki was suggesting grep -Z -El 'ABC|DFG' *.json | xargs -0 sed -ibak -e 's#ABC#123#g' -e 's#DFG#456#g'. In other words, add -Z to the options you're using with grep, and add -0 as an option to xargs. Commented Jul 13, 2023 at 15:19
  • Tried it but I get below sed: en 3.json en.json en 2.json : No such file or directory Commented Jul 13, 2023 at 16:00

3 Answers 3

2

The problem you're seeing is that the space is being treated as a separator.

A simple example:

% echo a b | xargs ls
ls: cannot access a: No such file or directory
ls: cannot access b: No such file or directory

We can see ls tried to look for files a and b.

Instead we should tell xargs and grep to use a different separator; the best one is the NUL characater (\0). To do this you can use -Z with grep and -0 with xargs.

So your command becomes

grep -Z .... | xargs -0 ....

For example:

$ ls
en 1.json  en 2.json  en.json
$ grep -Z -El ABC *.json | xargs -0 sed -ibak -e 's/ABC/HHH/'
$ ls
en 1.json  en 1.jsonbak  en 2.json  en 2.jsonbak  en.json  en.jsonbak

We can see the 3 files got modified ("bak" files created).

2
  • I tried it however now th issue is sed command is trreating all files as one and i get below error sed: en 3.json en.json en 2.json : No such file or directory Commented Jul 13, 2023 at 16:11
  • Then you possibly made a typo; I just ran an example (answer updated with the example).on my CentOS machine. You need both the grep -Z and xargs -0 for this to work. Commented Jul 13, 2023 at 17:02
0

If in doubt use a loop:

#!/usr/bin/env bash

while IFS= read -r file; do
    sed -ibak -e 's#ABC#123#g' -e 's#DFG#456#g' "$file"
done < <(grep -El 'ABC|DFG' *.json)

The above assumes your file names do not contain newlines.

0

You can use a JSON-aware tool such as jq to perform the replacements:

jq '."123" = ."ABC" | ."456" = ."DFG" | del(."ABC", ."DFG")'

Output

{
  "123": "/long_name",
  "456": "/{long_name}"
}

Applying this to all three files, and remembering to quote the filenames that contain spaces,

for file in en.json 'en 1.json' 'en 2.json'
do
    cp -p -- "$file" "$file.old"
    jq '."123" = ."ABC" | ."456" = ."DFG" | del(."ABC", ."DFG")'<"$file.old" >"$file" &&
        : rm -f -- "$file.old"
done

Remove the : from in front of rm to remove the saved original file(s).

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.