Как может я обрабатывать в пакетном режиме переименовать имена файлов так, чтобы они не включали символы, которые сталкиваются с другими файловыми системами что касается экземпляра,
Screenshot 2015-09-07-25:10:10
Обратите внимание, что двоеточия являются проблемой в этом имени файла. Они не будут переварены Windows или Mac.
Эти файлы могли быть переименованы к
Screenshot 2015-09-07-25--10--10
Я должен переместить большую сумму файлов от Ubuntu до другой ОС. Я скопировал их в использование диска NTFS Rsync, но это потеряло некоторые файлы. Я также скопировал их в диск ext4.
Следующий список является зарезервированными символами:
< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)
Другая проблема - то, что Windows не чувствителен к регистру когда дело доходит до имен файлов, (и большинство систем OS X также).
Вы могли сделать что-то как:
rename 's/[<>:"\\|?*]/_/g' /path/to/file
Это заменит все эти символы a _
. Обратите внимание, что Вы не должны заменять /
, так как это - недопустимый символ для имен файлов в обеих файловых системах, но используется в качестве разделителя пути Unix. Расширитесь на каталог и все его содержание с:
find /path/to/directory -depth -exec rename 's/[<>:"\\|?*]/_/g' {} +
Отметьте это оба /
(который отмечает конец шаблона), и \
оставлены. Для сохранения уникальности Вы могли добавить случайный префикс к нему:
$ rename -n 's/[<>:"\/\\|?*]/_/g && s/^/int(rand(10000))/e' a\\b
a\b renamed as 8714a_b
Больше полного решения должно, по крайней мере:
То есть, foo.mp3
не должен становиться foo.mp3.1
, но foo.1.mp3
, так как Windows более уверен в расширениях.
Имея это в виду, я записал следующий сценарий. Я пытался быть неразрушающим, при помощи пути префикса, в который я могу скопировать переименованные файлы, вместо того, чтобы изменить оригинал.
#! /bin/bash
windows_chars='<>:"\|?*'
prefix="windows/"
# Find number of files/directories which has this name as a prefix
find_num_files ()
(
if [[ -e $prefix$1$2 ]]
then
shopt -s nullglob
files=( "$prefix$1-"*"$2" )
echo ${#files[@]}
fi
)
# From http://www.shell-fu.org/lister.php?id=542
# Joins strings with a separator. Separator not present for
# edge case of single string.
str_join ()
(
IFS=${1:?"Missing separator"}
shift
printf "%s" "$*"
)
for i
do
# convert to lower case, then replace special chars with _
new_name=$(tr "$windows_chars" _ <<<"${i,,}")
# if a directory, make it, instead of copying contents
if [[ -d $i ]]
then
mkdir -p "$prefix$new_name"
echo mkdir -p "$prefix$new_name"
else
# get filename without extension
name_wo_ext=${new_name%.*}
# get extension
# The trick is to make sure that, for:
# "a.b.c", name_wo_ext is "a.b" and ext is ".c"
# "abc", name_wo_ext is "abc" and ext is empty
# Then, we can join the strings without worrying about the
# . before an extension
ext=${new_name#$name_wo_ext}
count=$(find_num_files "$name_wo_ext" "$ext")
name_wo_ext=$(str_join - "$name_wo_ext" $count)
cp "$i" "$prefix$name_wo_ext$ext"
echo cp "$i" "$prefix$name_wo_ext$ext"
fi
done
В действии:
$ tree a:b
a:b
├── b:c
│ ├── a:d
│ ├── A:D
│ ├── a:d.b
│ └── a:D.b
├── B:c
└── B"c
└── a<d.b
3 directories, 5 files
$ find a:b -exec ./rename-windows.sh {} +
mkdir -p windows/a_b
mkdir -p windows/a_b/b_c
mkdir -p windows/a_b/b_c
cp a:b/B"c/a<d.b windows/a_b/b_c/a_d.b
mkdir -p windows/a_b/b_c
cp a:b/b:c/a:D.b windows/a_b/b_c/a_d-0.b
cp a:b/b:c/A:D windows/a_b/b_c/a_d
cp a:b/b:c/a:d windows/a_b/b_c/a_d-1
cp a:b/b:c/a:d.b windows/a_b/b_c/a_d-1.b
$ tree windows/
windows/
└── a_b
└── b_c
├── a_d
├── a_d-0.b
├── a_d-1
├── a_d-1.b
└── a_d.b
2 directories, 5 files
Сценарий доступен в моем GitHub repo.
Сценарий ниже может использоваться для замены списка строк или символов, возможно происходящих на имя файла, произвольной заменой на строку. Так как сценарий только переименовывает сам файл (не путь), нет никакого риска питания с каталогами.
Замена определяется в списке: chars
(см. далее ниже). Возможно дать каждой строке свою собственную замену, смочь инвертировать переименование, если Вы когда-либо хотели бы сделать это. (принятие замены является уникальной строкой). В случае, если требуется заменить все проблематичные строки подчеркиванием, просто определите список как:
chars = [
("<", "_"),
(">", "_"),
(":", "_"),
('"', "_"),
("/", "_"),
("\\", "_"),
("|", "_"),
("?", "_"),
("*", "_"),
]
Для предотвращения дублированных имен сценарий сначала создает "новое" имя. Это затем проверяет, существует ли столь же именованный файл уже в том же каталоге. Если так, это создает новое имя, которому предшествуют dupe_1
или dupe_2
, пока это не находит "доступное" новое название файла:
становится:
#!/usr/bin/env python3
import os
import shutil
import sys
directory = sys.argv[1]
# --- set replacement below in the format ("<string>", "<replacement>") as below
chars = [
("<", "_"),
(">", "_"),
(":", "_"),
('"', "_"),
("/", "_"),
("\\", "_"),
("|", "_"),
("?", "_"),
("*", "_"),
]
# ---
for root, dirs, files in os.walk(directory):
for file in files:
newfile = file
for c in chars:
newfile = newfile.replace(c[0], c[1])
if newfile != file:
tempname = newfile; n = 0
while os.path.exists(root+"/"+newfile):
n = n+1; newfile = "dupe_"+str(n)+"_"+tempname
shutil.move(root+"/"+file, root+"/"+newfile)
rename_chars.py
.Тестовый прогон это на каталоге командой:
python3 /path/to/rename_chars.py <directory_to_rename>
Обратите внимание что в строке:
("\\", "_bsl_"),
в Python обратной косой черты должна оставить другая обратная косая черта.
Я думаю, что приведенные выше решения могут сработать, но у вас другой случай: файловая система ext допускает более длинные имена файлов и имена папок. Вы также должны учитывать длину имен и соединять их с помощью некоторого алгоритма, добавляя числа или что-то в конце в случае повторяющихся имен.