Jak zastąpić folder, którego nazwa jest datą, tj. RRRRMMDD, z hierarchią folderów według roku, miesiąca i daty?

8

Mam listę folderów, które mają daty nazwisk. Daty są w formacie RRRRMMDD (np. 20150129). W tych folderach znajdują się dokumenty tekstowe powiązane z tą konkretną datą.

Chciałbym zmienić ich strukturę w hierarchii folderów z roku na miesiąc i przesuwać dokumenty tekstowe do odpowiedniego folderu „data” niżej w hierarchii.

Innymi słowy, chciałbym, aby folder „root” został nazwany po roku takim jak 2015, a następnie utwórz podfoldery o nazwach takich jak 01, a następnie utwórz kolejne podfoldery o nazwach takich jak 29, które zawierają odpowiednie dokumenty tekstowe .

Więc ścieżka wyglądałaby jak 2015/01/29/file.txtlub 2015>01>29>file.txt.

Rzuciłem okiem na Automator i wydaje się, że coś takiego nie jest możliwe, chociaż mogę się mylić, więc chciałbym wiedzieć ...

  1. Czy istnieje jakieś łatwe rozwiązanie tego problemu, które każdy laik może zrozumieć, na przykład przepływ pracy Automatora, czy też wymaga to pewnego zrozumienia poleceń terminalowych i wyrażeń regularnych?

  2. Jak rozwiązać ten problem, pod warunkiem że istnieje rozwiązanie?

davidjnatarajan
źródło
Do kogo głosował, aby zamknąć to pytanie jako „zbyt ogólne”, dlaczego? Jestem ciekaw, co jest „zbyt szerokie” w tym pytaniu?
user3439894,
Czy wszystkie te foldery RRRRMMDD znajdują się bezpośrednio w jednym folderze głównym, czy są rozłożone w szerszej hierarchii?
nohillside
@patrix W moim przypadku wszystkie znajdują się w tym samym katalogu lub folderze głównym
davidjnatarajan

Odpowiedzi:

8

Zakładając, że wszystkie te foldery RRRRMMDD są częścią tego samego katalogu nadrzędnego, który można uruchomić

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
  • for d in */; doPętli odczytuje wszystkie wpisy Directory, spływu /zapewnia, że tylko nazwy katalogów rzeczywiście pasuje
  • [[ $d =~ [0-9]{8}/ ]] sprawdza, czy bieżący wpis składa się z 8 cyfr i kontynuuje od następnego wpisu, jeśli nie
  • ${d:0:4}/${d:4:2}/${d:6:2}używa rozszerzenia parametrów w bashcelu utworzenia ciągu zawierającego nową ścieżkę
  • --W obu mkdiri mvzapobiega problemu w przypadku katalogu lub pliku nazwa rozpoczyna się -. Nie może się tu zdarzyć, ale i tak jest to prawdopodobnie dobra praktyka.

Podziękowania dla @terdon i @ user3439894 za pomysły na ulepszenie oryginalnego skryptu.

nohillside
źródło
Dzięki za odpowiedź, działa idealnie! Wydaje mi się, że to rozwiązanie jest lepsze niż to oferowane przez @grgarside, ponieważ jest znacznie szybsze, szczególnie w przypadku ogromnego korpusu zawierającego tysiące dokumentów tekstowych.
davidjnatarajan
8

W terminalu można użyć następujących opcji. cddo zawierającego folder, a następnie uruchom następujące polecenie:

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 fuzyskuje rekursywnie każdy plik w bieżącym katalogu.
-exec bash -cotwiera powłokę, aby uruchomić następujące polecenia.
F=$(…)otwiera podpowłokę i używa sed na ścieżce pliku do manipulowania ścieżką do folderów.
^\./([0-9]{4})([0-9]{2})([0-9]{2})jest wyrażeniem regularnym z trzema grupami przechwytywania, takimi jak: zastępowanie, w którym każda grupa przechwytywania ( itd.) jest oddzielona . tworzy katalogi do przeniesienia plików. przenosi każdy plik do odpowiedniego folderu.
\1/\2/\3\1/
mkdir -p -- $(dirname "$F")
mv -- "$1" "$F"

Pobiera to hierarchię po lewej stronie i konwertuje ją do hierarchii po prawej:

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

Jeśli w folderze zawierającym są inne pliki z datą jako nazwą, zostaną one przeniesione tak, jakby były folderem. Aby temu zapobiec, zamień drugą linię na:

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

W (?:/.+)związku z tym zapewnia, że ścieżka ma kolejny komponent, ignorując wszystko bez dziecka w katalogu nadrzędnego, które są pliki.

grg
źródło
@klanomath regex101.com
grg
@grgarside Thanx
klanomath