Можно ли переименовать файлы в папке с именем родительской папки?
Это уникальная ситуация, которую лучше объяснить на примере
Пример 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
две подпапки в папке A
, содержимое B и C похоже на следующее
Должен быть преобразован в
Даже когда я запускаю алгоритм / скрипт / команду в папке A
Третье, что он должен сделать, это должен игнорировать скрытые файлы
пример
должно привести к
наконец, он должен работать на несколько папок одновременно
2 ответа
Есть несколько способов подойти к этому вопросу. Пожалуйста, внимательно прочитайте инструкцию для достижения наилучших результатов.
В этом ответе:
- Подход Python
find
+bash
подход- подход только для bash с globstar
1. Python решение
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
2. Решение с помощью команды find и флага -exec
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
,
Внутри команды 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
нет вывода, поэтому команда молчит.
3. Простой подход: Bash и Glob Star
Вышеупомянутые два подхода используют рекурсивный обход дерева. Поскольку в вашем примере у вас есть только два уровня в дереве каталогов (каталог фильмов и файлы), мы можем упростить нашу предыдущую команду оболочки следующим образом:
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
как только вы уверены, что будете делать то, что вы хотите).