Скопируйте большое количество файлов с новыми именами

У меня есть большое количество файлов (десятки тысяч), которые мне нужно скопировать из /dir1 в /dir2, но имя файла должно измениться как таковое:

СТАРОЕ ИМЯ ---> НОВОЕ ИМЯ

filename.txt ---> bob_filename_1253.txt

Где bob - это uid, которому принадлежит файл, а 1253 - это последний раз, когда он был изменен.

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

ОДНАКО, это занимает мучительно много времени. Есть ли более чистый и быстрый способ добиться этого?

Заранее спасибо!

3 ответа

Копирование *.txt файлы с переименованием из dir1/ к dir2/ каталог:

$ ls -l
всего 8
drwxr-xr-x 2 пользователь пользователь 4096 лют 19 01:06 dir1
drwxr-xr-x 2 пользователь пользователь 4096 лют 19 01:08 dir2

$ ls dir1
name1.txt name2.txt name3.txt
$ для пути к файлу в./dir1/*; 
    сделать username= $ (stat -c '%U' $filepath) && 
       filedate= $ (stat -c '%Y' $ путь к файлу) && 
       newpartname='dir2 /' $ n '_'"${filepath //'./ dir1 / '}" && 
       cp $filepath "${newpartname // '. txt'}"_ $ filedate.txt; 
    сделанный
ls dir2 /
имя_пользователя1_1582067151.txt имя_пользователя2_1582067158.txt имя_пользователя3_1582067165.txt

Вы говорите, что переименование занимает много времени. Но каков размер всех ваших файлов? Вы знаете время копирования всех ваших файлов?

Следующий сценарий должен выполнить свою работу. Попробуйте, и когда результат вас устроит, удалите комментарий # спереди cp -p "$oldpath""$f9" "$newpath""$new" делать реальную работу.

Скрипт создаст в вашем домашнем каталоге файл с именем ls_file.txt. Этот файл необходим для хранения вывода ls -l "$oldpath"команда, и сценарий должен будет прочитать ее во время выполнения. После завершения сценария выполнения, то ls_file.txt файл больше не нужен, и вы можете его удалить.

Всегда делайте резервную копию

Чтобы создать и использовать файл сценария, выполните следующие действия:

  1. Создайте и отредактируйте файл сценария в своем домашнем каталоге, выполнив следующую команду в терминале:

    nano ~/my_script.sh

  2. Скопируйте и вставьте следующий код в редактор, изменив /full/path/to/old/directory/ с полным путем к вашему /dir1 каталог и /full/path/to/new/directory/ на полный путь к вашему /dir2 каталог:

#!/bin/bash

oldpath="/full/path/to/old/directory/"
newpath="/full/path/to/new/directory/"

ls -l "$oldpath" > "ls_file.txt"

while read f1 f2 f3 f4 f5 f6 f7 f8 f9
                do
                        mod="${f6}${f7}${f8}"
                        mod2=$(echo "$mod" | tr -d ' :')
                        name="${f9%.*}"
                        ext="${f9##*.}"
                        new="$f3""_""$name""_""$mod2"".""$ext"
                        echo "$f9"" ---> Will be renamed ---> ""$new"
                        echo "----------------------------"
#                       cp -p "$oldpath""$f9" "$newpath""$new"
                done < "ls_file.txt"
  1. Сохраните файл сценария и выйдите из редактора, нажав Ctrl+, X затем нажмите Y.

  2. Сделайте файл сценария исполняемым, выполнив в терминале следующую команду:

    chmod +x ~/my_script.sh

  3. Запустите сценарий, выполнив в терминале следующую команду:

    bash ~/my_script.sh

Выполнено


Как это устроено?

Запуск сценария из моего домашнего каталога в тестовом каталоге dir1/ который содержит один файл file.txt и тестовый каталог dir2/ который пуст при указании oldpath="/home/raffa/test/dir1/" и newpath="/home/raffa/test/dir2/", следующий процесс является примером того, как работает скрипт:

  1. Команда ls -l /home/raffa/test/dir1/ выполняется, и вывод записывается в файл с именем ls_file.txt который затем будет содержать атрибуты и имена файлов в dir1/ в этом случае один файл file.txt вот так:
-rw-r--r-- 1 raffa raffa 0 Jan 25 19:49 file.txt
  • -rw-r--r-- это файловый режим.
  • 1 количество ссылок.
  • Первое raffa имя владельца.
  • Секунда raffa это название группы.
  • 0 размер файла.
  • Jan 25 19:49 (три поля) - дата и время последней модификации.
  • file.txt это имя файла.

. 2. Скрипт прочитает содержимое dir1/ из ls_file.txt построчно и разделите каждую строку на поля (всего девять) и присвойте значение каждого поля переменной f1 f2 f3 f4 f5 f6 f7 f8 f9.

  1. mod="${f6}${f7}${f8}" назначит дату и время последнего изменения, хранящиеся в переменных f6 f7 f8 к переменной mod поэтому он будет содержать такое значение:
Jan2519:49
  1. mod2=$(echo "$mod" | tr -d ' :') примет значение переменной $modудалите пробелы и двоеточия : и присвоить его переменной mod2 поэтому он будет содержать такое значение:
Jan251949
  1. name="${f9%.*}" присвоит переменной имя файла без расширения name поэтому он будет содержать такое значение:
file
  1. ext="${f9##*.}" присвоит расширение файла без имени переменной ext поэтому он будет содержать такое значение:
txt
  1. new="$f3""_""$name""_""$mod2"".""$ext" отформатирует новое имя файла как owner_name_lastmodified.extension и присвоить его переменной new поэтому он будет содержать такое значение:
raffa_file_Jan251949.txt
  1. cp -p "$oldpath""$f9" "$newpath""$new" скопирует исходный файл file.txt из dir1/ к dir2/ переименовав его в raffa_file_Jan251949.txt и попытается сохранить исходные атрибуты файла во время этого процесса, поэтому он будет выполняться следующим образом:
cp -p /home/raffa/test/dir1/file.txt /home/raffa/test/dir2/raffa_file_Jan251949.txt

Этот процесс будет повторяться для всех файлов в dir1/ с любым расширением.


Другой формат времени:

Если вы предпочитаете, чтобы формат даты и времени последнего изменения был таким 20200125133815 где 2020 это год и 01 это месяц и 25 это день и 13 это час в 24 формате и 38 это минуты и 15 это секунды, поэтому ваши скопированные имена файлов будут выглядеть так raffa_file_20200125133815.txt, затем замените приведенный выше код сценария следующим кодом:

#!/bin/bash

oldpath="/full/path/to/old/directory/"
newpath="/full/path/to/new/directory/"


ls -l --time-style="+%Y%m%d%H%M%S" "$oldpath" > "ls_file.txt"

while read f1 f2 f3 f4 f5 f6 f7
                do
                        mod="${f6}"
                        name="${f7%.*}"
                        ext="${f7##*.}"
                        new="$f3""_""$name""_""$mod"".""$ext"
                        echo "$f7"" ---> Will be renamed ---> ""$new"
                        echo "----------------------------"
#                       cp -p "$oldpath""$f7" "$newpath""$new"
                done < "ls_file.txt"

Уведомление:

В некоторых случаях анализ вывода ls можно было бы лучше избежать.

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

printf '%s\0' dir1/*.txt | xargs -r0 cp -np -t dir2/

выполнить копию, затем

cd dir2/

printf '%s\0' *.txt | xargs -r0 rename -n -- '
  $_ = join "_", ( getpwuid( (stat $_)[4] ) )[0], $_, (stat $_)[9]
'

переименовать (удалить -n от rename как только вас устраивают предложенные изменения).


Хотя чаще всего он используется для простых s/pattern/replacement/ изменения имени, основанный на Perl rename Команда, доступная в текущих версиях Ubuntu, может фактически переименовывать файлы на основе практически произвольных выражений Perl.

В этом случае мы можем использовать встроенный в Perl stat чтобы получить числовой UID и mtime, а затем используйте getpwuid чтобы превратить UID в имя пользователя. (stat $_)[9] время модификации (mtime) в секундах эпохи - если вам это нужно в другом формате, вы можете использовать POSIX::strftime или один из нескольких других модулей управления временем.

Другие вопросы по тегам