1

I have a group of json files. Each file contains one json object and they all have the same schema. There is a field in the json that I want to rename the file to. How can I do that?

I came up with this solution:

find . -name '*.json' | xargs -i mv {} $(cat {} | jq '.billingAccountList[0]' | tr -d \").json

But it doesn't work, because cat is trying to interpret the {}. I was hoping the xargs would interpret it instead. It gives me this error:

cat: {}: No such file or directory

3 Answers 3

2

Use a loop to handle the files one by one.

For instance, with while read:

find . -name '*.json' | while read fname; do
    newname=$(jq -r '.billingAccountList[0]' "${fname}").json
    mv "${fname}" "${newname}"
done

Using for might be possible, but it's more sensitive to spaces in the names of the files:

for fname in $(find . -name '*.json'); do
    ... (same as above) ...

Also note that you're moving the files into the current directory, as the original path is being stripped, so if you want to keep the directory structure:

find . -name '*.json' | while read fname; do
    fdir=$(dirname "${fname}")
    newname=$(jq -r '.billingAccountList[0]' "${fname}").json
    mv "${fname}" "${fdir}/${newname}"
done

UPDATE: Using jq -r as suggested by @steeldriver. Thanks!

2

You should be able to use jq -r to output raw (unquoted) strings - rather than stripping quotes with tr

Also I'm going to assume that you want to rename the files relative to their parent directory (so, for example, if the value .billingAccountList[0] in path/to/file.json is foo, then the new name should be path/to/foo.json rather than foo.json), and that your implementation of find has a -execdir

So I'd do something like

find . -name '*.json' -execdir sh -c '
  echo mv -- "$1" "$(jq -r ".billingAccountList[0]" < "$1").json"
' find-sh {} \;

or (at least with GNU find) more efficiently

find . -name '*.json' -execdir sh -c '
  for f; do echo mv "$f" "$(jq -r ".billingAccountList[0]" < "$f").json"; done
' find-sh {} +

Remove the echo once you are satisfied that it's doing the right thing.

2
  • This looks interesting. Why do you need the -- there and what's the find-sh for? Commented Jul 27, 2018 at 16:15
  • 1
    @DanielKaplan actually with hindsight you probably don't need the --, it's just to prevent mv from treating the filename as an option if it should happen to start with a hyphen - but I was forgetting that find -execdir will prefix the found files with ./ anyway. find-sh is just an arbitrary string passed as the shell's $0 positional parameter, so that for example error messages from the shell command are easier to attribute. (Probably a more useful thing to add to the mv in place of -- would be -n or --no-clobber to prevent accidental overwriting of an existing file.) Commented Jul 27, 2018 at 16:24
0

With zsh's zmv (for better handling of conflicts):

autoload zmv
zmv -n '(**/)*.json(#qD.)' '$1$(jq -r '.billingAccountList[0]' < $f).json

(remove -n when happy).

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.