Renomeia um arquivo para o nome do diretório pai no terminal

4

Eu tenho que lidar com um grande número de arquivos aninhados dentro de diretórios (um arquivo por diretório), parecido com algo como isto:

fred/result.txt
john/result.txt
mary/result.txt
...

Atualmente, estou usando o seguinte comando para processar cada um desses arquivos:

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c \"cd '{}' && processResult result.txt\" \;

Eu estou procurando por algo que eu possa adicionar no final desse comando que irá alterar os nomes dos arquivos para fred.txt , etc. e depois mova o arquivo para o diretório pai para eliminar a camada extra de diretórios.

Qual seria a melhor maneira de fazer isso?

    
por DTR 16.03.2016 / 22:18

5 respostas

2

Podemos usar a abordagem clássica: find com while read combo:

Demo:

$ ls *
fred:
results.txt

jane:
results.txt

john:
results.txt

$> find . -type f -name "results.txt" -printf "/%P\n" | while read FILE ; do DIR=$(dirname "$FILE" );\                            
>  mv ."$FILE" ."$DIR""$DIR".txt;done                                                                                             
$> ls
fred/  jane/  john/
$> ls *
fred:
fred.txt

jane:
jane.txt

john:
john.txt

Use echo antes de usar mv para testar a linha com seus arquivos.

Menor melhoria

Podemos incorporar a chamada a bash em exec, com expansão de parâmetro (especificamente, remoção de prefixo). Basicamente, ele está incorporando um script na chamada exec.

$> find "$PWD" -type f -name "results.txt" -exec bash -c ' DIR=$( dirname "{}"  ); echo "{}" "$DIR"/"${DIR##*/}".txt  ' \;                
/home/xieerqi/TESTDIR/fred/results.txt /home/xieerqi/TESTDIR/fred/fred.txt
/home/xieerqi/TESTDIR/jane/results.txt /home/xieerqi/TESTDIR/jane/jane.txt
/home/xieerqi/TESTDIR/john/results.txt /home/xieerqi/TESTDIR/john/john.txt

Para tornar o comando geral, (trabalhando para qualquer arquivo, não apenas results.txt ), basta remover -name "results.txt" part. Veja também esta postagem para uma solução alternativa em Python.

    
por Sergiy Kolodyazhnyy 16.03.2016 / 23:53
3

Acho que isso é legítimo (usando o comando rename baseado em perl)

find dir -name 'results.txt' -exec rename -nv -- 's|/(.*)/(.*)$|//.txt|' {} +

por exemplo. dado

$ tree dir
dir
├── fred
│   └── results.txt
├── john
│   └── results.txt
└── mary
    └── results.txt

3 directories, 3 files

então

$ find dir -name 'results.txt' -exec rename -v -- 's|/(.*)/(.*)$|//.txt|' {} +
dir/fred/results.txt renamed as dir/fred/fred.txt
dir/john/results.txt renamed as dir/john/john.txt
dir/mary/results.txt renamed as dir/mary/mary.txt

resultando em

$ tree dir
dir
├── fred
│   └── fred.txt
├── john
│   └── john.txt
└── mary
    └── mary.txt

3 directories, 3 files

Se você precisar ir 1 nível abaixo de dir , talvez seja possível simplesmente fazer

rename -vn -- 's|/(.*)/(.*)$|//.txt|' dir/*/results.txt

Como sempre, a opção -n é deixada para fins de teste

    
por steeldriver 16.03.2016 / 23:33
3

O pequeno script python abaixo fará exatamente como você descreve; vai:

  • renomeie qualquer arquivo em determinado diretório, denominado results.txt , de acordo com seu diretório superior
  • mova um nível (diretório) para cima
  • exclua o diretório (agora vazio) em que o arquivo estava inicialmente localizado

O resultado:

Isto:

directory
|
├── fred
│   └── results.txt
├── john
│   └── results.txt
└── mary
    └── results.txt

se tornará:

directory
|
├── fred.txt
│   
├── john.txt
│ 
└── mary.txt

O script

#!/usr/bin/env python3
import shutil
import os
import sys

dr = sys.argv[1]

for root, dirs, files in os.walk(dr):
    for file in files:
        if file == "results.txt":
            spl = root.split("/"); newname = spl[-1]; sup = ("/").join(spl[:-1])
            shutil.move(root+"/"+file, sup+"/"+newname+".txt"); shutil.rmtree(root)

Para usar

  • Copie o código abaixo em um arquivo vazio, salve-o como move_rename.py
  • Execute-o pelo comando:

    python3 /path/to/move_rename.py <directory_to_reorganize>
    

Nota

como sempre, primeiro teste em uma pequena amostra

    
por Jacob Vlijm 16.03.2016 / 22:55
2

Fazê-lo através da descoberta parece excessivamente complicado. Por que não usar um loop for simples?

for folder in *; do
    (cd "$folder" && processResult result.txt)  # This replaces your previous find(1)
    mv "$folder/result.txt" "${folder}.txt"
    rmdir "$folder"
done
    
por Ángel 17.03.2016 / 01:56
1

Se você tiver vários arquivos em pastas com as mesmas extensões, alguns dos comandos acima substituirão todos menos um, renomeando todos os arquivos com o mesmo nome.

folder1/file1.txt file2.txt --> folder1/folder1.txt

Para evitar isso, você pode usar este comando ou equivalente:

for f in */*.txt ;do fp=$(dirname "$f"); fl=$(basename "$f"); mv "$fp/$fl" "$fp/$fp"_"$fl"; done

Mantendo o nome de arquivo completo $ fl em vez de apenas a extensão, você preserva todos os arquivos.

folder1/file1.txt file2.txt --> folder1/folder1_file1.txt folder1_file2.txt
    
por tofner 28.10.2017 / 11:55