Можно ли переименовать файлы в папке с именем родительской папки?

Это уникальная ситуация, которую лучше объяснить на примере

Пример 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 ответа

Решение

Есть несколько способов подойти к этому вопросу. Пожалуйста, внимательно прочитайте инструкцию для достижения наилучших результатов.

В этом ответе:

  1. Подход Python
  2. find + bash подход
  3. подход только для 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 как только вы уверены, что будете делать то, что вы хотите).

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