
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.txt
con el valor del title
parámetro del fileX.json
archivo 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.txt
tambié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 jsonpipe
es 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.sed
grep
Una posible mejora obvia es utilizar printf
un campo entero relleno con ceros para cambiar el nombre de los archivos a nombres numerados de ancho fijo, por ejemplo, Untitled-0001.txt
en 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 mv
comando queharíausar. Edítelo para eliminar el echo
de antes de cada mv
comando 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 json
se le asigna el nombre de archivo JSON correspondiente reemplazando el .txt
sufijo del nombre de archivo con .json
.
Si el nombre del archivo generado corresponde a un archivo existente, jq
se utiliza para analizar ese archivo JSON. Crea un comando de shell para cambiar el nombre del archivo actual al valor de cadena de la .title
clave 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 mv
GNU coreutils y luego usar su --backup
opción.
Modifica la jq
expresión en
jq -r --arg name "$name" '[ "mv", "-v", "--backup=numbered", "--", $name, .title ] | @sh' "$json"
Las copias de seguridad numeradas con GNU mv
significan agregar sufijos de nombre de archivo como .~1~
, .~2~
etc. al final de los archivos respaldados. También agregué la -v
opción a GNU mv
aquí para obtener algunos resultados sobre lo que mv
está haciendo, como
renamed 'file.txt' -> 'Untitled' (backup: 'Untitled.~2~')