Cambiar el nombre de los archivos según el contenido JSON

Cambiar el nombre de los archivos según el contenido JSON

Tengo dos conjuntos de archivos:

file1.txt     file1.json 
file2.txt     file2.json    
file3.txt     file3.json  
...
fileN.txt     fileN.json

Los archivos JSON contienen el siguiente formato:

{ "ago": "59 sec ago",  "base_time": 1401243133,  "title": "Untitled",  "type": "None",  "retrieval_time": 1401624105,  "id": "qwNAgvYZ" }

Quiero cambiar el nombre de cada uno fileX.txtcon el valor del titleparámetro del fileX.jsonarchivo respectivo.

Por ejemplo,

rename fileX.txt -> Untitled

Puedo filtrar ese valor de la siguiente manera;

cat fileX.json | awk -F"\"" '{print $10}'

Pero si encuentro un nombre de archivo existente, debería cambiarle el nombre con un sufijo.

Por ejemplo, digamos que existe un Untitled.txt. Por lo tanto, el nuevo archivo debería renombrarse como Untitled-1.txt. El próximo encuentro Untitled-2.txttambién.

Respuesta1

Aquí hay un script de shell bastante simple y directo que usajsonpipepara hacer lo que quieras. No utiliza ninguna característica sofisticada de sh/bash y solo realiza la comprobación mínima de cordura de los nombres de archivos.

NOTA:jqes mucho más capaz que jsonpipe, pero jsonpipees más simple y fácil de usar cuando no te importa (o no quieres saber) la estructura de los datos json y solo quieres extraer uno o dos campos, y/o quieres usar json datos con herramientas de procesamiento de texto orientadas a líneas como awk, etc.sedgrep

Una posible mejora obvia es utilizar printfun campo entero relleno con ceros para cambiar el nombre de los archivos a nombres numerados de ancho fijo, por ejemplo, Untitled-0001.txten lugar de Untitled-1.txt. Te lo dejo para que lo hagas si lo deseas.

Tal como está escrito, en realidad no cambiará el nombre de ningún archivo. Sólo imprimirá el mvcomando queharíausar. Edítelo para eliminar el echode antes de cada mvcomando para que realmente cambie el nombre de los archivos.

#! /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"
      else
        # 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"
        else
          # 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"
        fi
      fi
    fi
  fi
done

Ejemplos de uso/Prueba de que funciona:

$ 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

Respuesta2

for name in file*.txt; do
    json=${name%.txt}.json
    if [ -f "$json" ]; then
        eval "$(
            jq -r --arg name "$name" '[ "mv", "--", $name, .title ] | @sh' "$json"
        )"
    fi
done

Esto recorre todos los nombres en el directorio actual que coinciden con el patrón file*.txt. Para cada uno de estos nombres, a la variable jsonse le asigna el nombre de archivo JSON correspondiente reemplazando el .txtsufijo del nombre de archivo con .json.

Si el nombre del archivo generado corresponde a un archivo existente, jqse utiliza para analizar ese archivo JSON. Crea un comando de shell para cambiar el nombre del archivo actual al valor de cadena de la .titleclave en el documento. El shell evalúa el comando generado que cambia el nombre del archivo.

No se realiza ninguna verificación de la validez del archivo JSON y no se manejan las colisiones de nombres.

La forma más sencilla de manejar las colisiones de nombres sería asegurarse de que está utilizando mvGNU coreutils y luego usar su --backupopción.

Modifica la jqexpresión en

jq -r --arg name "$name" '[ "mv", "-v", "--backup=numbered", "--", $name, .title ] | @sh' "$json"

Las copias de seguridad numeradas con GNU mvsignifican agregar sufijos de nombre de archivo como .~1~, .~2~etc. al final de los archivos respaldados. También agregué la -vopción a GNU mvaquí para obtener algunos resultados sobre lo que mvestá haciendo, como

renamed 'file.txt' -> 'Untitled' (backup: 'Untitled.~2~')

información relacionada