2017-01-05 2 views
0

У меня есть куча файлов в структуре каталогов, как например:Попытки переименовать определенные типы файлов в рекурсивных каталогах

Dir 
    SubDir 
      File 
      File 
    Subdir 
      SubDir 
         File 
      File 
    File 

Извините за беспорядочное форматирование, но, как вы можете видеть, что есть файлы на все другой каталоге уровни. Все эти имена файлов содержат в себе строку из 7 номеров: 1234567_filename.ext. Я пытаюсь удалить число и подчеркивание в начале имени файла.

Сейчас я использую Баш и с помощью этого Oneliner переименовывать файлы, используя мв и разрезают:

for i in *; do mv "$i" "$(echo $i | cut -d_ -f2-10)"; done 

Это время работать, пока я CD'd в каталог. Мне очень хотелось бы найти способ сделать это рекурсивно, чтобы он только переименовал файлы, а не папки. Кроме того, я использовал цикл по каждому элементу в оболочке, вне Баша для каталогов, которые имеют кучу папок с файлами в них, и никаких других подкаталогах как таковые:

foreach$ set p=`echo $f | cut -d/ -f1` 
foreach$ set n=`echo $f | cut -d/ -f2 | cut -d_ -f2-10` 
foreach$ mv $f $p/$n 
foreach$ end 

Но это работает только тогда, когда нет других подкаталогов в папках.

Есть ли цикл или oneliner, который я могу использовать для переименования всех файлов в каталогах? Я даже пытался использовать find, но не мог понять, как включить cut в код.

Любая помощь очень ценится.

+0

Вы хотите удалить номер и '_'? – Inian

+0

Стреляйте, да извините, вот-вот нужно было отредактировать сообщение, чтобы прояснить это. – VanCityGuy

+0

'set' не используется для установки обычных переменных, а' end' не является ключевым словом 'bash'. – chepner

ответ

0

bash действительно предоставляет функции, и они могут быть рекурсивными, но для этой работы вам не нужна рекурсивная функция. Вам просто нужно перечислить все файлы в дереве. Команда find может сделать это, но включение globstar опции bash «s и с помощью Glob оболочки, чтобы сделать это безопаснее:

#!/bin/bash 

shopt -s globstar 

# enumerate all the files in the tree rooted at the current working directory 
for f in **; do 
    # ignore directories 
    test -d "$f" && continue 

    # separate the base file name from the path 
    name=$(basename "$f") 
    dir=$(dirname "$f") 

    # perform the rename, using a pattern substitution on the name part 
    mv "$f" "${dir}/${name/#???????_/}" 
done 

Обратите внимание, что это не проверяет, что имена файлов на самом деле соответствует шаблону, указанному перед выполнением переименование; Я беру вас за ваше слово, что они делают. Если такой чек был нужен, его можно было бы добавить.

+0

Это выглядит как победитель. Спасибо всем за вашу помощь, но это работало лучше для меня. Если бы я хотел изменить это, чтобы удалить пробелы и заменить символами подчеркивания из всех полных путей файла, я сделаю что-то вроде этого: 'mv" $ f "" $ {dir// _ /}/$ {name// _/} "' – VanCityGuy

+0

Это выглядит как более тяжелая версия решения, размещенная в нижней части моего ответа. Вам действительно нужно использовать внешние инструменты 'basename' и' dirname', когда у вас есть расширение параметра? Как насчет исключения имен файлов, таких как 'abcdefg_abcd.ext', т. Е. Без номеров? – ghoti

+0

@VanCityGuy - нет, вы, вероятно, не сделали бы этого ... Если вы попытаетесь «mv» foo bar/baz «" foo_bar/baz "', а целевой каталог еще не существует, mv завершится с ошибкой. – ghoti

2

С переименованием в Perl (автономные команды):

shopt -s globstar 
rename -n 's|/[0-9]{7}_([^/]+$)|/$1|' **/* 

Если все выглядит нормально удалить -n.

globstar: Если установлено, то шаблон ** используется в контексте расширительного пути будет матч всех файлов и ноль или несколько каталогов и подкаталоги. Если , за шаблоном следует /, только каталоги и подкаталоги .

+1

@ Иниан: Спасибо. Я обновил свой ответ и исправил ошибку. Стало сложнее предотвратить переименование каталогов. – Cyrus

+0

Я пробовал это, и он высказал ошибку, указав, что опция -n недействительна для 'rename'. Я использую bash 4.2 в терминальной оболочке mac osx. Возможно, это несовместимо. Я должен был дать больше информации. Но спасибо за все усилия! – VanCityGuy

+0

@VanCityGuy: Я предполагаю, что вы не используете реализацию переименования Perl [This] (http://hints.macworld.com/article.php?story=20050630022203488). – Cyrus

0

Как насчет этой небольшой подстройки к тому, что у вас есть уже:

for i in `find . -type f`; do mv "$i" "$(echo $i | cut -d_ -f2-10)"; done 

В основном просто поменять местами * с `найти. -тип f`

0

Должно быть возможно, используя find ...

find -E . -type f \ 
    -regex '.*/[0-9]{7}_.*\.txt' \ 
    -exec sh -c 'f="${0#*/}"; mv -v "$0" "${0%/*}/${f#*_}"' {} \; 

Ваши find варианты могут быть разные - я делаю это в FreeBSD. Идея состоит в следующем:

  • -E инструктирует find использовать расширенные регулярные выражения.
  • -type f вызывает только обычные файлы (не каталоги или символические ссылки).
  • -regex ... соответствует файлам, которые вы ищете. Вы можете сделать это более конкретным, если вам нужно.
  • exec ... \; выполняет команду, используя {} (файл, который мы нашли) в качестве аргумента.

Команда, с которой мы работаем, сначала использует расширение параметров, чтобы захватить целевой каталог, а второй - удалить имя файла. Обратите внимание на временную переменную $f, которая используется для решения вопроса о том, что дополнительные символы подчеркивания являются частью имени файла.

Обратите внимание, что это НЕ команда bash, хотя вы можете, конечно, запустить ее из оболочки bash. Если вы хотите Баш решения, которое не требует использования внешнего инструмента, как find, вы можете сделать следующее:

$ shopt -s extglob # use extended glob format 
$ shopt -s globstar # recurse using "**" 
$ for f in **/+([0-9])_*.txt; do f="./$f"; echo mv "$f" "${f%/*}/${f##*_}"; done 

Это использует ту же логику, что и find решения, но использует Баш v4 extglob к обеспечить лучшее совпадение имен файлов и globstar для рекурсии через подкаталоги.

Надеюсь, что эта помощь.