копировать и переименовывать файлы по номеру в предыдущем файле - PullRequest
0 голосов
/ 26 февраля 2019

Добрый день,

У меня есть целевой каталог со следующими файлами:

V1__baseline.sql
V2__inserts.sql
V3__packages.sql
...
V10_change_table.sql

Затем у меня есть исходный каталог со следующими файлами:

v000_001_mk-tbl-dwa_ranking.sql
v000_002_mk-tbl-dwa_camp_week.sql
...
...
v000_179_crt_table_stat_flg.sql
v000-180_crt_table_ing_flg.sql
v000-181_crt_table_update_flg.sql

Что яхотел бы скопировать все файлы сейчас или в будущем после v000_179_crt_table_stat_flg.sql из источника в место назначения и последовательно переименовать файлы в каталоге назначения.Каталог назначения должен выглядеть следующим образом:

V1__baseline.sql
V2__inserts.sql
V3__packages.sql
...
V10__change_table.sql
V11__crt_table_ing_flg.sql
V12__crt_table_update_flg.sql

Другими словами, формат имени файла в месте назначения - V{number}__{name}.sql, тогда как формат имени файла в источнике - v000-{number}_{name}.sql

Как мне это сделать?Я предполагаю, что мне понадобится умный зацикленный сценарий с командой примерно такой:

cp "`ls -Art ${source_dir}/* | tail -n 1`"  ${destination_dir}/

Ответы [ 2 ]

0 голосов
/ 27 февраля 2019

Поскольку в вопросе указывается переименование и копирование, а не переименование и перемещение файлов, решение должно предположительно убедиться, что файлы из исходного каталога не продублированы в месте назначения.Это усложняет решение.

Сценарий не может просто проверить наличие исходного файла в месте назначения, поскольку он был переименован как часть перемещения.Выполнение cmp или diff, вероятно, приводит к расточительному использованию ресурсов, особенно если сравниваемые файлы представляют собой большие дампы базы данных (на что указывает расширение .sql).

В приведенном ниже решении я добавилфайл манифеста, чтобы отследить, какие файлы были скопированы, но если бы я создавал это для себя, мне не понравился бы такой подход.Если файл манифеста был случайно удален или отредактирован, скрипт потеряет отслеживание того, какие файлы уже были скопированы, и при следующем запуске все файлы будут скопированы.Последовательная индексация имен файлов в каталоге назначения будет исключена.Если возможно, я думаю, что было бы лучше:

  • Переименовать исходные файлы, чтобы отразить их скопированный статус, после чего исключить эти файлы из операции копирования
  • Переименовать и переместить файлыот исходного каталога до места назначения

Обратите внимание, что при выполнении числовых сравнений bash видит числа с ведущими нулями как восьмеричные.Вы можете удалить начальные нули при извлечении числа для сравнения, но я использовал $((10#$foo)) в условиях теста, чтобы указать десятичные числа.Я думаю, что это испортило подсветку синтаксиса Переполнения стека - что неправильно трактует текст после # в 10# как комментарий.

#!/bin/bash

# Set source and destination paths
readonly SRC=src
readonly DEST=dest
readonly COPY_MANIFEST="${SRC}"/copied.txt

# $COPY_MANIFEST will keep track of which files have been copied
[[ -f "$COPY_MANIFEST" ]] || touch "$COPY_MANIFEST"

# Get the highest index in destination directory from the file numeric prefix
highest=0
for file in $DEST/*; do
    base=$(basename ${file})
    index=$(echo $base | sed 's/[^0-9]//g')
    # Compare numbers. Convert to decimal format because leading zeros denote octal numbers 
    [[ $((10#$highest)) -le $((10#$index)) ]] && highest=$index
done

# Rename and copy files from source to destination
for original in ${SRC}/*; do
    previously_copied=false

    # Don't process the manifest file
    [[ ${original} = $COPY_MANIFEST ]] && continue

    # If the source directory is empty, exit early
    [[ -f "$original" ]] || { echo "No source files in ${SRC}"; exit;}

    # Check the file has not already been copied - uses a manifest file rather 
    # than using tools like cmp or diff to check for duplicate files.
    while read line; do
        if [[ "${original}" = "${line}" ]]; then
            echo "${original} has already been renamed and copied."
            previously_copied=y
        fi
    done < "$COPY_MANIFEST"
    [[ $previously_copied = y ]] && continue

    # Get the base name of the file
    name=$(basename ${original})

    # Original question asks that all files greater than v000_179_crt_table_stat_flg.sql are copied.
    # If this requirement is not needed, the next 2 lines can be removed
    num=$(echo "$name" | sed 's/V[0-9]*_\([0-9]*\).*/\1/g')
    [[ $((10#$num)) -le $((179)) ]] && { echo "Not eligible"; continue; }

    # Build the new filename and copy
    # Get rid of the prefix, leaving the descriptive name
    name=${name#V[0-9]*_[0-9]*_}
    highest=$(( 10#$highest + 1 ))
    new_name=V${highest}__${name}
    cp ${original} ${DEST}/${new_name}

    # Update the manifest to prevent repeat copying
    echo ${original} >> $COPY_MANIFEST
done
0 голосов
/ 26 февраля 2019

Грубая версия -

targetDir=. # adjust as needed
declare -i ctr=1
declare -a found=()
declare -l file
for file in [Vv][0]*            # refine this to get the files you want
do x=${file#v}                  # knock off the leading v
   while [[ "$x" =~ ^[0-9_-] ]] # if leading digits/dashes/underscores
   do x=${x:1}                  # strip them
   done
   found=( V${ctr}__* )         # check for existing enumerator
   while [[ -e "${found[0]}" ]] # if found
   do (( ctr++ ))               # increment
      found=( V${ctr}__* )      # and check again
   done
   mv "$file" "$targetDir/V${ctr}__$x" # move the file
done

Пожалуйста, прочтите, задайте вопросы и отредактируйте в соответствии с вашими потребностями.

...