I have two sets of files;
file1.txt file1.json
file2.txt file2.json
file3.txt file3.json
fileN.txt fileN.json

json files contain the following format:
{"ago": "59 sec ago", "base_time": 1401243133, "title": "Untitled", "type": "None", "retrieval_time": 1401624105, "id": "qwNAgvYZ"}

I want to rename each fileX.txt with the vale of "title" parameter from the respective fileX.json file.

for ex.

rename fileX.txt -> Untitled

I can filter that value out as follows; $ cat fileX.json | awk -F"\"" '{print $10}' But if I encounter an existing filename, it should rename itself with an suffix.

For example, let's say there is an existing Untitled.txt. So the new file should be renamed as Untitled-1.txt. Next encounter as Untitled-2.txt likewise.

  • do you want us to do your homework ? – Rahul May 17 '16 at 16:04
  • yes, not my homework though ;) but a project I'm working on. – user3148655 May 17 '16 at 16:10
  • 1
    Are you sure you want/need to do this in bash? You really should use something that knows how to parse JSON instead of assuming it'll be, say, $10. You could write a python or perl script or find a command line tool that understands it rather than a line oriented tool like awk – Eric Renouf May 17 '16 at 16:22
  • Eric, its fine if i can use such a parser. appreciate if you could assist me – user3148655 May 17 '16 at 16:48
  • 1
    jq is a good json parser for use in shell scripts. And there are other tools like jsonpipe that convert json to line-oriented files suitable for use with awk, sed, grep, etc. Both are available packaged for most linux distros, as are json-parsing libraries for python, perl, and other lanaguages. – cas May 18 '16 at 2:16

Here's a fairly simple and straight-forward shell script that uses jsonpipe to do what you want. It doesn't use any fancy sh/bash features, and does only the bare minimum sanity checking of filenames.

NOTE: jq is far more capable than jsonpipe, but jsonpipe is simpler and easier to use when you don't particularly care (or want to know) about the structure of the json data and only want to extract one or two fields, and/or want to use json data with line-oriented text processing tools like awk, sed, grep etc.

One obvious possible enhancement is to use printf with a zero-padded integer field to rename files to fixed-width numbered names, e.g. Untitled-0001.txt rather than Untitled-1.txt. I'll leave that for you to do if you want it.

As written, it won't actually rename any file. It will only print the mv command that it would use. Edit it to remove the echo from before each mv command to make it actually rename the files.

#! /bin/sh

for f in file*.txt ; do
  b=$(basename "$f" .txt)

  # ignore current .txt file if there's no matching .json file
  if [ -e "$b.json" ] ; then
    # extract the title field.
    title=$(jsonpipe < "$b.json" | 
            awk -F'\t' '$1=="/title" {gsub(/\"/,"",$2) ; print $2}')

    if [ -n "$title" ] ; then
      if [ ! -e "$title.txt" ] ; then
        echo mv -v "$f" "$title.txt"
        # are there any other "$title-*.txt" filenames?
        others=$(find . -maxdepth 1 -name "$title-*.txt")
        if [ -z "$others" ] ; then
          echo mv -v "$f" "$title-1.txt"
          # use version-sort to get highest $title- number used.
          highest=$(printf "%s\n" "$others" | sort -V | tail -n 1)
          hnum=$(printf "%s\n" "$highest" | sed -e 's/^.*-// ; s/\.txt$//')
          hnum=$(( highest_num + 1))
          echo mv -v "$f" "$title-$hnum.txt"

Examples of use / Proof that it works:

$ ls -l
total 8
-rw-rw-r-- 1 cas cas 132 May 19 23:47 file1.json
-rw-rw-r-- 1 cas cas   0 May 20 00:04 file1.txt
-rwxrwxr-x 1 cas cas 797 May 20 00:04 json-rename.sh

$ cat file1.json 
{"ago": "59 sec ago", "base_time": 1401243133, "title": "Untitled",
 "type": "None", "retrieval_time": 1401624105, "id": "qwNAgvYZ"}

$ ./json-rename.sh 
mv -v file1.txt Untitled.txt

$ touch Untitled.txt
$ ./json-rename.sh 
mv -v file1.txt Untitled-1.txt

$ touch Untitled-1.txt
$ ./json-rename.sh 
mv -v file1.txt Untitled-2.txt

$ touch Untitled-999.txt
$ ./json-rename.sh 
mv -v file1.txt Untitled-1000.txt
  • works like a charm. to remove the files with "/" i have added the following code segement: title=${title////_}; – user3148655 May 21 '16 at 4:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.