На этот вопрос уже есть ответ здесь:
Это уникальная ситуация, которая можно лучше объяснить на примере
Пример 1
скажем, у меня есть папка с именем A
, B.mkv
и C.srt
] - это два файла в папке, я хочу переименовать эти файлы в A.mkv
и A.srt
.
практическим примером может быть папка под названием American Sniper (2014)
и American.Sniper.2014.1080p.BluRay.x264.YIFY.mkv
и American.Sniper .2014.Subtitle.BluRay.ShaAnig.srt
- это два файла в папке, нужно переименовать эти файлы в American Sniper (2014) .mkv
и American Sniper (2014) .srt
Пример 2
Это должно быть рекурсивно
скажем, у меня есть папка с именем A
, B
и C
- это два Sub -папки в папке A
.содержимое B и C выглядит следующим образом
Должно быть преобразовано в
Даже когда я запускаю алгоритм / сценарий / команду в папке A
Третье, что он должен сделать, это игнорировать скрытые файлы
пример
должен привести к
, наконец, он должен работать с несколькими папками одновременно
Существует несколько способов приблизиться к этому вопросу. Прочитайте инструкции тщательно для лучших результатов.
В этом ответе:
find
+ bash
подходPython является довольно мощным языком для системного администрирования, и пересекающее дерево каталогов может быть сделано через os.walk()
функция. В сценарии, представленном ниже, мы делаем точно, что - мы находим все файлы и воздействуем на каждого из них, путем определения полного пути в каждый файл, расширение файла, и переименования его через os.rename()
функция.
#!/usr/bin/env python3
import os,sys
def get_all_files(treeroot):
for dir,subdirs,files in os.walk(treeroot):
for f in files:
if f in __file__: continue
fullpath = os.path.realpath( os.path.join(dir,f) )
extension = fullpath.split('.')[-1]
newpath = os.path.join(dir, dir + '.' + extension)
print('Move ' + fullpath + ' to ' + newpath )
# os.rename(fullpath,newpath)
def main():
top_dir="."
# If directory not given, assume cwd
if len(sys.argv) == 2: top_dir=sys.argv[1]
get_all_files(top_dir)
if __name__ == '__main__' : main()
Примечание: очень очень важный то, что для фактического переименования файлов необходимо удалить #
прежде # os.rename(fullpath,newpath)
.
Все стандартные правила для сценариев применяются: - сохраняют его как add_location_name.py
в каталоге самого верхнего уровня - делают исполняемый файл с chmod +x ./add_location_name.py
- выполненный с ./add_location_name.py
Вот пример того, как это работает на практике. Я создал каталог с двумя другими, Movie A (2016)
и Movie B (2016)
. В них у них обоих есть два файла. Наш сценарий живет в том же каталоге:
$ tree
.
├── add_location_name.py
├── Movie A (2014)
│ ├── filea.mkv
│ └── fileb.srt
└── Movie B (2016)
├── filea.mkv
└── fileb.srt
Таким образом, когда мы запускаем скрипт, мы будем видеть следующий вывод:
$ ./add_location_name.py
Move /home/xieerqi/testdir/Movie A (2014)/fileb.srt to ./Movie A (2014)/./Movie A (2014).srt
Move /home/xieerqi/testdir/Movie A (2014)/filea.mkv to ./Movie A (2014)/./Movie A (2014).mkv
Move /home/xieerqi/testdir/Movie B (2016)/fileb.srt to ./Movie B (2016)/./Movie B (2016).srt
Move /home/xieerqi/testdir/Movie B (2016)/filea.mkv to ./Movie B (2016)/./Movie B (2016).mkv
find
команда полезна во многих отношениях, конкретна при выполнении операций на нескольких уровнях дерева каталогов. В данном случае мы можем использовать его, чтобы отфильтровать все файлы и выполнить операцию переименования на них.
В первую очередь, позвольте мне дать Вам решение:
find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}" \;
Взгляды, долгие и страшные, правильно? Но не волнуйтесь, отпускает по тому, как это работает.
В первую очередь, необходимо распознать, что существует две вещи, продолжающиеся: find
команда определяет местоположение всех файлов и bash
часть - то, что на самом деле делает часть переименования. На внешней стороне, что мы видим, простая команда:
find -type f -exec <COMMAND> \;
Это только находит все файлы (никакие каталоги или символьные ссылки) и называет некоторую другую команду каждый раз, когда она находит файл. В этом случае определенная КОМАНДА, которую мы имеем, bash
.
Таким образом, что делает bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}"
часть делает? Ну, в первую очередь, распознайте структуру: bash -c 'command1;command2' arg0 arg1
. Каждый раз, когда -c
флаг используется первый параметр командной строки arg0
будет установлен как $0
, окружите имя, таким образом, это не важно, но "{}"
важно. Это - заключенный в кавычки заполнитель для имени файла это find
передаст как аргумент bash
.
На внутренней части удара управляют, чтобы мы извлекли путь к файлу fp=$(dirname "$1")
, имя каталога fn=$(basename "$fp")
, и расширение файла или префикс px="${1##*.}"
. Все это удается действительно приятно, так как мы выполняем команду от каталога самого верхнего уровня (очень важный!). Наконец, mv "$1" "$fp"/"$fn"."$px"' sh "{}"
переименует исходный файл это find
дал нам новому имени файла, с чем мы растем "$fp"/"$fn"."$px"
использование всех тех переменных.
Тест команды выполняется на том же каталоге как прежде:
$ tree
.
├── Movie A (2014)
│ ├── filea.mkv
│ └── fileb.srt
└── Movie B (2016)
├── filea.mkv
└── fileb.srt
2 directories, 4 files
И если мы выполняем команду, с echo
вместо mv
мы видим, что каждое имя файла переименовано соответственно.
$ find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";echo "$1" "$fp"/"$fn"."$px"' sh ">
./Movie A (2014)/fileb.srt ./Movie A (2014)/Movie A (2014).srt
./Movie A (2014)/filea.mkv ./Movie A (2014)/Movie A (2014).mkv
./Movie B (2016)/fileb.srt ./Movie B (2016)/Movie B (2016).srt
./Movie B (2016)/filea.mkv ./Movie B (2016)/Movie B (2016).mkv
Помните: вышеупомянутое использование команды echo
только для тестирования. Когда Вы используете mv
нет никакого вывода, таким образом, команда тиха.
Вышеупомянутые два подхода используют рекурсивный обход дерева. С тех пор в Вашем примере у Вас только есть два уровня в дереве каталогов (каталог фильма и файлы), мы можем упростить нашу предыдущую команду оболочки как так:
for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done
Снова, та же идея - замена echo
с mv
когда Вы уверены, что это работает правильно.
Результаты испытаний являются тем же:
$ tree
.
├── add_location_name.py
├── Movie A (2014)
│ ├── filea.mkv
│ └── fileb.srt
└── Movie B (2016)
├── filea.mkv
└── fileb.srt
2 directories, 5 files
$ for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done
Movie A (2014)/filea.mkv Movie A (2014)/Movie A (2014).mkv
Movie A (2014)/fileb.srt Movie A (2014)/Movie A (2014).srt
Movie B (2016)/filea.mkv Movie B (2016)/Movie B (2016).mkv
Movie B (2016)/fileb.srt Movie B (2016)/Movie B (2016).srt
Учитывая
$ tree
.
└── A
├── B
│ ├── somefile
│ ├── T.txt
│ ├── X.srt
│ └── Z.mkv
└── C
├── somefile
├── T.txt
├── W.mkv
└── Y.srt
3 directories, 8 files
затем
$ find A -type f \( -name '*.mkv' -o -name '*.srt' \) -not -name '.*' -exec sh -c '
for f; do
dir="${f%/*}" ; ext="${f##*.}" ; new="${dir##*/}"
echo mv -- "$f" "${dir}/${new}.${ext}"
done' sh {} +
mv -- A/C/Y.srt A/C/C.srt
mv -- A/C/W.mkv A/C/C.mkv
mv -- A/B/Z.mkv A/B/B.mkv
mv -- A/B/X.srt A/B/B.srt
(удаляют echo
, после того как Вы уверены , это собирается сделать то, что Вы хотите).