Renomeie arquivos com base no conteúdo JSON

Renomeie arquivos com base no conteúdo JSON

Eu tenho dois conjuntos de arquivos:

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

Os arquivos JSON contêm o seguinte formato:

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

Quero renomear cada um fileX.txtcom o valor do titleparâmetro do respectivo fileX.jsonarquivo.

Por exemplo,

rename fileX.txt -> Untitled

Posso filtrar esse valor da seguinte maneira;

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

Mas se eu encontrar um nome de arquivo existente, ele deverá renomear-se com um sufixo.

Por exemplo, digamos que exista um arquivo Untitled.txt. Portanto, o novo arquivo deve ser renomeado como Untitled-1.txt. Próximo encontro da Untitled-2.txtmesma forma.

Responder1

Aqui está um script de shell bastante simples e direto que usajsonpipepara fazer o que quiser. Ele não usa nenhum recurso sofisticado de sh/bash e faz apenas a verificação mínima de integridade dos nomes de arquivos.

OBSERVAÇÃO:jqé muito mais capaz que jsonpipe, mas jsonpipeé mais simples e fácil de usar quando você não se importa particularmente (ou deseja saber) sobre a estrutura dos dados json e deseja extrair apenas um ou dois campos e/ou deseja usar json dados com ferramentas de processamento de texto orientadas a linhas , como awk,, etc.sedgrep

Um possível aprimoramento óbvio é usar printfum campo inteiro preenchido com zeros para renomear arquivos para nomes numerados de largura fixa, por exemplo, Untitled-0001.txtem vez de Untitled-1.txt. Vou deixar isso para você fazer, se quiser.

Conforme está escrito, na verdade não renomeará nenhum arquivo. Ele imprimirá apenas o mvcomando que eleseriausar. Edite-o para remover o echode antes de cada mvcomando para que ele realmente renomeie os arquivos.

#! /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

Exemplos de uso / Prova 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

Responder2

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

Isso percorre todos os nomes no diretório atual que correspondem ao padrão file*.txt. Para cada nome, a variável jsonrecebe o nome de arquivo JSON correspondente, substituindo o .txtsufixo do nome do arquivo por .json.

Se o nome do arquivo gerado corresponder a um arquivo existente, jqserá usado para analisar esse arquivo JSON. Ele cria um comando shell para renomear o arquivo atual para o valor da string da .titlechave no documento. O shell avalia o comando gerado que renomeia o arquivo.

Nenhuma verificação é feita quanto à validade do arquivo JSON e as colisões de nomes não são tratadas.

A maneira mais fácil de lidar com colisões de nomes seria certificar-se mvde que você está usando o GNU coreutils e, em seguida, usar sua --backupopção.

Modifique a jqexpressão em

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

Backups numerados com GNU mvsignificam adicionar sufixos de nome de arquivo como .~1~, .~2~, etc. ao final dos arquivos de backup. Eu também adicionei a -vopção GNU mvaqui para obter alguma saída sobre o que mvestá fazendo, como

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

informação relacionada