Замена точек в имени файла символами подчеркивания, кроме расширения

9

Может кто-нибудь подсказать, как переименовать имя файла:

head.body.date.txt

Для того, чтобы:

head_body_date.txt

Есть ли однострочный оператор для переименования в Unix ?

SG
источник
9
Вы предполагаете, что .tar.gzэто расширение? Кроме того, добро пожаловать в U & L!
EKons
2
Вы действительно имеете в виду Unix, а не Linux? Какой Unix?
Тердон
1
Ответ очевиден mv head.body.date.txt head_body_date.txt. Если у вас есть другие ограничения или реальные примеры, пожалуйста, обновите ваш вопрос с ними. В настоящее время неясно, относится ли вопрос к файлу с таким явным именем, к единственному каталогу, в котором все файлы имеют определенный формат, или вы хотите найти иерархию каталогов для конкретных имен и изменить их.
Кусалананда

Ответы:

9

Переберите имена файлов и используйте расширение Parameter для преобразования:

for f in *.*.*.txt; do i="${f%.txt}"; echo mv -i -- "$f" "${i//./_}.txt"; done

Шаблон расширения параметра ${f//./_}заменяет все .s на _s в имени файла ( $f).

Выше приведен пробный прогон, чтобы фактическое переименование произошло, удалите echo:

for f in *.*.*.txt; do i="${f%.txt}"; mv -i -- "$f" "${i//./_}.txt"; done

Если вы хотите иметь дело с любым расширением, а не просто .txt:

for f in *.*.*.*; do pre="${f%.*}"; suf="${f##*.}"; \
                     echo mv -i -- "$f" "${pre//./_}.${suf}"; done

После проверки удалите echoдля фактического действия:

for f in *.*.*.*; do pre="${f%.*}"; suf="${f##*.}"; \
                     mv -i -- "$f" "${pre//./_}.${suf}"; done

Общий, для произвольного числа точек, хотя бы одна:

for f in *.*; do pre="${f%.*}"; suf="${f##*.}"; \
                 mv -i -- "$f" "${pre//./_}.${suf}"; done
heemayl
источник
Есть ли общий способ, если существует произвольное количество точек?
Ciprian Tomoiagă
1
@CiprianTomoiaga Do:for f in *.*; do ...; done
Heemayl
Я мог бы предложить поместить *.*образец прямо в пример, поскольку теперь код выглядит так, как будто он работает только с фиксированным числом точек.
ilkkachu
@ilkkachu Достаточно справедливо, отредактировано.
Heemayl
Большое спасибо heemay, это сработало для меня. и немного обучения!
SG
4

с perlоснованнымrename

$ rename -n 's/\.[^.]+$(*SKIP)(*F)|\./_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)
  • \.[^.]+$(*SKIP)(*F) пропустите этот шаблон и ищите альтернативные совпадения
  • |\./_/gзаменить все .на_

Или, используя негативный взгляд

$ rename -n 's/\.(?![^.]+$)/_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)

Как только это будет в порядке, удалите -nопцию

Sundeep
источник
3
rename 's/\.(?=[^.]*\.)/_/g' *.txt

Использует регулярное выражение, чтобы найти все, кроме последнего экземпляра .в имени файла (без захвата), и заменить их на _. Обобщает, *.*если хотите.

(эта конкретная версия renameкажется установлена ​​через util-linux). Я пользуюсь Ubuntu 12.04(да, сильно устаревшая машина).

Джонатан Кэрролл
источник
2

bash:

old=head.body.date.txt oldb=${old%.*} olde=${old##*.} ; \
mv -- "$old" "${oldb//./_}.${olde}"
АРУ
источник
(1) Вы должны использовать mv --, чтобы защититься от «старых» имен файлов, которые начинаются с -. (2) Вы должны поместить $oldв кавычки ( "$old"), чтобы защититься от «старых» имен файлов, которые содержат пробелы или символы, которые являются специальными для оболочки (например, *и ?). (3) Ваш ответ терпит неудачу в несколько патологическом случае, когда расширение содержит подчеркивание: head.body.date.t_xtпереименовывается в head_body_date_t.xt.
Скотт
@ Скотт, спасибо. Код теперь доработан по всем трем упомянутым пунктам.
agc
1

Использование mv, sedи revв одной строке:

mv "head.body.date.txt" "$(echo head.body.date.txt | rev | sed 's/\./_/2g' | rev)"

Если вы хотите применить его ко всем txtфайлам в текущем представлении, использование globs кажется сложным из-за того mv, как работает, но вы можете сделать forцикл в одну строку:

for file in *.txt; do mv "$file" "$(echo $file | rev | sed 's/\./_/2g' | rev)"; done

Чуть дольше, но вы можете сопоставить несколько шаблонов в вашем цикле!

Валентин Б.
источник
2
(1) Аааааа! Не делай $(ls …); просто делай for file in *.txt. (2) Вы должны использовать больше цитаты: mv "$file" "$(…)".
Скотт
Спасибо, Скотт, должным образом отмечен и отредактирован! Я все еще молод в ракушке, будьте нежны;)
Валентин Б.
Оглядываясь назад, использование, renameкак некоторые другие предложили в своих ответах, звучит проще, хотя ...
Валентин Б.
-1
 mv head.body.date.txt head_body_date.txt

Это так же просто, как и для одного файла за раз. Вы не указываете, хотите ли вы, чтобы это делалось рекурсивно внутри каталога или для всего каталога, так что это было бы вашим наиболее удобным и быстрым решением.

coderodour
источник