Como substituir uma pasta com o nome é uma data, ou seja, AAAAMMDD pela hierarquia de pastas ano, mês, data?

8

Eu tenho uma lista de pastas com datas para nomes. As datas estão no formato AAAAMMDD (por exemplo, 20150129). Dentro dessas pastas, há documentos de texto relacionados a essa data específica.

Eu gostaria de reestruturá-los em uma hierarquia de pastas que vai de ano para mês e data e mover os documentos de texto para a pasta 'date' correspondente, mais abaixo na hierarquia.

Em outras palavras, eu gostaria que a pasta 'root' fosse nomeada após o ano como 2015 e, em seguida, crie subpastas nomeadas com meses como 01 e, em seguida, crie subpastas nomeadas com datas como 29, que contêm os documentos de texto correspondentes .

Portanto, o caminho seria semelhante 2015/01/29/file.txtou 2015>01>29>file.txt.

Dei uma olhada no Automator e parece que algo assim não é possível, embora eu possa estar errado, então gostaria de saber ...

  1. Existe alguma solução fácil para esse problema que qualquer leigo possa entender, por exemplo, um fluxo de trabalho do Automator, ou isso requer alguma compreensão dos comandos do terminal e expressões regulares?

  2. Como alguém resolveria esse problema, desde que haja realmente uma solução?

davidjnatarajan
fonte
Para quem votou para encerrar esta questão como "muito ampla", por quê? Estou curioso para saber o que é "amplo demais" nessa questão?
user3439894
Essas pastas AAAAMMDD estão todas diretamente dentro de uma pasta mestre ou estão espalhadas por uma hierarquia mais ampla?
nohillside
@patrix No meu caso eles são todos no mesmo diretório ou mestre pasta
davidjnatarajan

Respostas:

8

Supondo que todas essas pastas AAAAMMDD façam parte do mesmo diretório pai que você pode executar

cd PARENT_DIRECTORY
for d in */; do
    [[ $d =~ [0-9]{8}/ ]] || continue
    mkdir -p -- "${d:0:4}/${d:4:2}"
    mv -- "$d" "${d:0:4}/${d:4:2}/${d:6:2}"
done
  • O for d in */; doloop lê todas as entradas do diretório, o final /garante que apenas os nomes dos diretórios realmente correspondam
  • [[ $d =~ [0-9]{8}/ ]] testa se a entrada atual consiste em 8 dígitos e continua com a próxima entrada, se não
  • ${d:0:4}/${d:4:2}/${d:6:2}usa expansão de parâmetro dentro bashpara criar uma sequência que contém o novo caminho
  • O problema --em ambos mkdire mvevita que o nome do diretório ou arquivo comece com a -. Isso não pode acontecer aqui, mas provavelmente é uma boa prática de qualquer maneira.

Agradecemos a @terdon e @ user3439894 por idéias sobre como melhorar o script original.

nohillside
fonte
Obrigado pela resposta, isso funciona perfeitamente! Eu sinto que essa solução é melhor do que a fornecida pelo @grgarside porque é muito mais rápida, especialmente quando se lida com um corpus massivo, incluindo milhares de documentos de texto.
Davidjnatarajan
8

Você pode usar o seguinte no Terminal. cdpara a pasta que contém e execute o seguinte:

find . -type f -exec bash -c \
  'F=$(sed -E "s#^\./([0-9]{4})([0-9]{2})([0-9]{2})#\1/\2/\3#" <<< $1);\
  mkdir -p -- $(dirname "$F");\
  mv -- "$1" "$F"' - {} \;

find . -type fobtém todos os arquivos no diretório atual recursivamente.
-exec bash -cabre um shell para executar os seguintes comandos.
F=$(…)abre um subshell e usa sed no caminho do arquivo para manipular o caminho para as pastas.
^\./([0-9]{4})([0-9]{2})([0-9]{2})é uma regex com três grupos de captura, como a seguir: é substituição, onde cada grupo de captura ( , etc) é separado por . cria os diretórios para mover os arquivos. move cada arquivo para sua pasta correspondente.
\1/\2/\3\1/
mkdir -p -- $(dirname "$F")
mv -- "$1" "$F"

Isso pega a hierarquia à esquerda e a converte na hierarquia à direita:

├── 20170201               └── 2017
   └── abcdefghij             ├── 02
└── 20170302                      └── 01
    └── abcdefghij 2                  └── abcdefghij
                               └── 03
                                   └── 02
                                       └── abcdefghij 2

Se houver outros arquivos na pasta que contém uma data como nome, eles serão movidos como se fossem uma pasta. Para evitar isso, substitua a segunda linha por:

  'F=$(sed -E "s#^\./([0-9]{4})([0-9]{2})([0-9]{2})(?:/.+)#\1/\2/\3#" <<< $1);\

Os (?:/.+)garante que o caminho tem um componente posterior, portanto, ignorando qualquer coisa sem uma criança no diretório pai que são arquivos.

grg
fonte
@klanomath regex101.com
grg
@grgarside Thanx
klanomath