Переместите файлы в правильные каталоги даты на основе имени файла

Некоторые из моих файлов находятся в неправильных каталогах, и я пытаюсь переместить их в нужное место.

пример:

каталог 20180622 будет содержать только имена файлов, имеющие в своем имени 20180622

если каталог 20180622 содержит файл с 20180623 в его имени, то это неуместный файл и должен перейти в соответствующий правильный каталог, а именно структура каталогов 20180623 исправлена ​​(к счастью)

date1/a/b/someprefix.date1.somesuffix #no problem
date1/a/b/someprefix.date2.somesuffix # problem
date2/c/d/someprefix.date2.somesuffix # no problem
date2/e/f/someprefix.date3.somesuffix # problem

date1/a/b/someprefix.date1.somesuffix
date2/a/b/someprefix.date2.somesuffix # problem fixed
date2/c/d/someprefix.date2.somesuffix
date3/e/f/someprefix.date3.somesuffix #problem fixed

используя find . -type f, я получаю список всех файлов, но не понимаю, как mv правильно разместить файлы. someprefix может быть чем угодно (также может содержать точку, поэтому вырезать - не лучший способ извлечь дату из имени файла) $f =~ (.*)(201[5-8][0-9][0-9][0-9][0-9][0-9])(.*) - это то, что я пытаюсь извлечь из имени файла.


person pythonRcpp    schedule 23.06.2018    source источник
comment
Всегда ли someprefix и somesuffix одинаковы?   -  person choroba    schedule 23.06.2018
comment
к сожалению, нет :(, это может быть любой текст, я могу только гарантировать, что дата всегда будет содержаться, однако некоторые суффиксы всегда заканчиваются на .gz. Я могу запустить несколько команд для всего набора некоторых префиксов, если есть решение с этим   -  person pythonRcpp    schedule 23.06.2018
comment
Учитывая, что Python является частью вашего имени пользователя, я предлагаю использовать его вместо Bash для решения этой проблемы. Это будет проще реализовать.   -  person John Zwinck    schedule 23.06.2018
comment
Вы можете узнать, как управлять параметрами bash здесь: gnu. org/software/bash/manual/html_node/   -  person Hielke Walinga    schedule 23.06.2018


Ответы (2)


К сожалению, при сопоставлении регулярных выражений bash вы не можете извлечь все подсовпадения, поэтому я возвращаюсь к grep, чтобы найти все даты.

find . -type f -print0 |
  while IFS= read -d "" -r filename; do
    mapfile -t dates < <(echo "$filename" | grep -Eo '\<201[5-8][0-9]{4}\>')
    if [[ ${#dates[@]} -eq 2 ]] && [[ ${dates[0]} != ${dates[1]} ]]; then
      destdir=$(dirname "$filename" | sed "s/${dates[0]}/${dates[1]}/")
      mkdir -p "$destdir"
      mv -v "$filename" "$destdir"
    fi
  done

Тестирование:

$ tree
.
├── 20180621
│   └── a
│       └── b
│           ├── a.20180621.txt
│           └── foo.20180701.bar
└── 20180701
    └── c
        └── d
            └── ok.20180701

6 directories, 3 files

У нас есть один файл, который нужно переместить

$ find . -type f -print0 |
   while IFS= read -d "" -r filename; do
     mapfile -t dates < <(echo "$filename" | grep -Eo '\<201[5-8][0-9]{4}\>')
     if [[ ${#dates[@]} -eq 2 ]] && [[ ${dates[0]} != ${dates[1]} ]]; then
       destdir=$(dirname "$filename" | sed "s/${dates[0]}/${dates[1]}/")
       mkdir -p "$destdir"
       mv -v "$filename" "$destdir"
     fi
   done
'./20180621/a/b/foo.20180701.bar' -> './20180701/a/b/foo.20180701.bar'

и результат

$ tree
.
├── 20180621
│   └── a
│       └── b
│           └── a.20180621.txt
└── 20180701
    ├── a
    │   └── b
    │       └── foo.20180701.bar
    └── c
        └── d
            └── ok.20180701

8 directories, 3 files

Не полагаясь на grep, и это настройка ответа Арунта:

find 20+([0-9])/ -type f -print0 |
    while IFS= read -d "" -r filename; do
        dirdate=${filename%%/*}
        if [[ "$(basename "$filename")" =~ 20[0-9]{6} ]]; then
            filedate=${BASH_REMATCH[0]}
            if [[ $dirdate != $filedate ]]; then
                dest=${filename/$dirdate/$filedate}
                echo mkdir -p "$(dirname "$dest")"
                echo mv -v "$filename" "$dest"
            fi
        fi
    done
person glenn jackman    schedule 23.06.2018

С ним должно быть довольно легко справиться, вам просто нужен цикл и если.

for path in $(find . -type f); do
    dirdate=$(echo $path | cut -d '/' -f 2)
    filedate=$(basename $path | cut -d '.' -f 2)

    if [[ $dirdate != $filedate ]]; then
        mv $path $(dirname $path | sed "s/$dirdate/$filedate/g")
    fi
done

Идея здесь чрезвычайно проста: он зацикливается на файлах, получает полный путь к файлу (./date1/a/b/prefix.date.suffix) и проверяет, равен ли date1 date. Если нет, переместите файл по тому же пути, где date1 был заменен на date.

Изменить для комментариев

Если вы хотите обрабатывать несколько разделителей для ваших имен файлов, вам просто нужно изменить строку filedate=, например:

filedate=$(basename $path | awk -F'[._\-]' '{print $2}' 2> /dev/null)

Это немного сложнее, но, скажем, это Bash.

2> /dev/null должен отключить предупреждения awk.

person Arount    schedule 23.06.2018
comment
да, это почти работает, но префикс и суффикс могут иметь разделитель dot . или - или _, поэтому вырезать дату из имени файла для меня сейчас проблема. мне нужно регулярное выражение, которое дает мне filenamedate=$(echo $path | cut -d '/' -f 5), теперь скажем, я получаю какой-то текст_дата-какой-то текст или домтекст-дата-какой-то текст или какой-то текст.дата.какой-то текст и т. д. Итак, регулярное выражение, которое может извлекать шаблон 201 [5-8] [0-9] [0- 9][0-9][0-9] сверху было бы идеально. дата всегда в формате ГГГГММДД, разделенная чем-то на обоих концах [что-нибудь] ГГГГММДД [что-нибудь] - person pythonRcpp; 23.06.2018
comment
что-то вроде $f =~ (.*)(201[5-8][0-9][0-9][0-9][0-9][0-9])(.*) не уверен, просто добавлю сюда как попытку - person pythonRcpp; 23.06.2018