Как я могу переименовать имена файлов, чтобы они не включали символы, которые столкнулись с другими файловыми системами, например,
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
Это заменит все эти символы на _. Обратите внимание, что вам не нужно заменять /, так как это недопустимый символ для имен файлов в обеих файловых системах, но используется как разделитель пути 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.
Вы можете сделать что-то вроде:
rename 's/[<>:"\\|?*]/_/g' /path/to/file
Это заменит все эти символы на _. Обратите внимание, что вам не нужно заменять /, так как это недопустимый символ для имен файлов в обеих файловых системах, но используется как разделитель пути 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.
. Сценарий ниже может использоваться для замены списка строк или символов, возможно, встречающихся в имени файла, произвольным замена на строку. Поскольку сценарий только переименовывает сам файл (а не путь), нет никакого риска возиться с каталогами.
Замена определена в списке: 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)
python3 /path/to/rename_chars.py <directory_to_rename>
Обратите внимание, что в строке:
("\\", "_bsl_"),
в python обратная косая черта должен быть экранирован другой обратной косой чертой.
. Сценарий ниже может использоваться для замены списка строк или символов, возможно, встречающихся в имени файла, произвольным замена на строку. Поскольку сценарий только переименовывает сам файл (а не путь), нет никакого риска возиться с каталогами.
Замена определена в списке: 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)
python3 /path/to/rename_chars.py <directory_to_rename>
Обратите внимание, что в строке:
("\\", "_bsl_"),
в python обратная косая черта должен быть экранирован другой обратной косой чертой.