Bash Script para classificar arquivos em pastas alfabéticas no ReadyNAS Duo v1

4

Eu tenho um antigo ReadyNAS Duo v1 (Sparc) com um sabor desconhecido do Linux.

Eu tenho uma estrutura de pastas com mais de 1.000 arquivos que desejo mover para uma estrutura de pastas com base na primeira letra do nome do arquivo (sem distinção entre maiúsculas e minúsculas).

Idealmente, gostaria que a estrutura de arquivos fosse assim:

myfiles-+
        +-A
          + Apple.txt
          + avocado.txt
        +-B
          + Banana.txt
          + broccoli.txt
etc. etc.

Eu tive um Google, mas só encontrei ferramentas de GUI.

Isso pode ser feito por meio da linha de comando ou de um script?

    
por BaronGrivet 27.01.2014 / 02:37

5 respostas

7

Aqui está um pequeno folheto que fará o que você quer:

$ mkdir -p output/{A..Z}; for i in tstdir/*; do export FILE=$(basename "$i");  LTR=$(echo" ${FILE:0:1}" | tr [a-z] [A-Z]); mv "$i" "output/$LTR/$FILE" ; done

Aqui está o mesmo comando na forma expandida para que você possa ver o que está acontecendo:

$ mkdir -p output/{A..Z}
$ for i in tstdir/*; do 
    FILE=$(basename "$i")  
    LTR=$(echo "${FILE:0:1}" | tr [a-z] [A-Z])
    mv "$i" "output/$LTR/$FILE"
  done

Detalhes

O primeiro acima assume que o diretório de saída de apenas as letras não existe e, portanto, irá criá-lo,

$ mkdir -p output/{A..Z}

O loop for funciona da seguinte maneira, percorrendo todos os arquivos em tstdir/* . Em seguida, ele determina o basename desse caminho e o armazena na variável $FILE . Cada iteração através do loop é armazenada na variável $i .

FILE=$(basename "$i")

Em seguida, usamos a capacidade do Bashes para retornar o primeiro caractere da variável nomeada, $FILE , e depois usar tr para converter qualquer caractere minúsculo em superior.

LTR=$(echo "${FILE:0:1}" | tr [a-z] [A-Z])

Quebrando isso um pouco mais:

$ echo "${FILE:0:1}"
s
$ echo "${FILE:0:1}"
T

Com o código tr , agora você pode ver o que está acontecendo:

$ echo "${FILE:0:1}" | tr [a-z] [A-Z]
S
$ echo "${FILE:0:1}" | tr [a-z] [A-Z]
T

O restante do comando apenas move os arquivos para o diretório de primeira letra correspondente.

Exemplo

Digamos que tenhamos este diretório de arquivos:

$ touch {a-z}file {A-Z}file

$ tree tstdir/ | head -10
tstdir/
|-- afile
|-- Afile
|-- bfile
|-- Bfile
|-- cfile
|-- Cfile
|-- dfile
|-- Dfile
|-- efile
...

Depois de executar o um forro:

$ tree output/ | head -10
output/
|-- A
|   |-- afile
|   '-- Afile
|-- B
|   |-- bfile
|   '-- Bfile
|-- C
|   |-- cfile
|   '-- Cfile
...
    
por 27.01.2014 / 03:42
1

com zsh

mkmv() {mkdir -p -- $argv[-1]:h && mv "$@"}
autoload zmv
zmodload zsh/files

zmv -Qp mkmv '(?)*(^-/)' '${(U)1}/$f'
    
por 27.01.2014 / 15:11
1

Experimente este script:

for first in $(ls -1 | sed 's/^\(.\).*$//' | tr '[a-z0-9]' '[A-Z0-9]' | uniq)
do
    mkdir tmp
    mv "$first"* tmp/
    lower=$(echo $first | tr '[A-Z]' '[a-z]')
    mv "$lower"* tmp/
    mv tmp/ "$first";
done

A lógica é a seguinte:

  1. Listar todos os arquivos no diretório atual.
  2. Extraia o primeiro caractere com sed .
  3. Altere o primeiro caractere para minúscula com tr , isso é para reduzir o número de comparações.
  4. Remover duplicatas com uniq .
  5. Crie um diretório temporário e mova todos os arquivos que iniciam com o caractere para essa pasta.
  6. Renomeie o arquivo temporário.

Eu usei esses arquivos para testar o script:

Apple.txt
avocado.txt
banana.txt
broccoli.txt
car.txt
dddd.txt
delete.txt
zaad.txt
zdfa.txt

Aqui está o resultado:

.
├── A
│   ├── Apple.txt
│   └── avocado.txt
├── B
│   ├── banana.txt
│   └── broccoli.txt
├── C
│   └── car.txt
├── D
│   ├── dddd.txt
│   └── delete.txt
└── Z
    ├── zaad.txt
    └── zdfa.txt

5 directories, 10 files
    
por 27.01.2014 / 03:30
0

Aqui está uma abordagem de força bruta:

for i in {a..z}; do 
 upper=$(printf "%s\n" $i | tr '[a-z]' '[A-Z]'); 
 ls -1 | grep -i "^$i" 2>/dev/null 1>&2 && mkdir -p $upper && 
 mv "$i*" "$u*" $u/ 2>/dev/null; 
done 

Isso iterará todas as letras ( {a..z} ), as converterá em maiúsculas ( tr '[a-z]' '[A-Z]' ), verificará se algum arquivo começa com essa letra ( ls -1 | grep -i $i ) e, em caso afirmativo, criará o diretório com a letra maiúscula nome ( mkdir -p $upper ) e, em seguida, mova todos os arquivos que começam com a letra em questão (em minúsculas e maiúscula) para a pasta correspondente.

Como você está executando isso em um NAS e provavelmente tem um shell mínimo instalado, o formato {a..z} pode não funcionar. Se assim for, faça isso em vez disso (eu disse que era uma abordagem de força bruta):

for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do 
 upper=$(printf "%s\n" $i | tr '[a-z]' '[A-Z]'); 
 ls -1 | grep -i "^$i" 2>/dev/null 1>&2 && mkdir -p $upper && 
 mv "$i*" "$u*" $u/ 2>/dev/null; 
done 
    
por 27.01.2014 / 17:10
0

Para melhorar a resposta do SLM, você também pode fazer isso funcionar para arquivos que começam com um número criando diretórios 0-9:

mkdir -p output/{A..Z}; mkdir -p output/{0..9}; for i in tstdir/*; do export FILE=$(basename "$i"); LTR=$(echo "${FILE:0:1}" | tr [a-z] [A-Z]); mv "$i" "output/$LTR/$FILE" ; done
    
por 03.09.2015 / 06:00