Преобразование всех расширений файлов в нижний регистр
Я пытаюсь скрыть все мои расширения, независимо от того, что это такое. До сих пор, из того, что я видел, вы должны указать, какие расширения файлов вы хотите преобразовать в нижний регистр. Тем не менее, я просто хочу скрыть все после first последней точки .
в имени.
Как это сделать в bash
?
Ответы
Ответ 1
Решение
Вы можете решить задачу в одной строке:
find . -name '*.*' -exec sh -c '
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
Примечание: это будет разбито на имена файлов, которые содержат символы новой строки. Но пока не медведь со мной.
Пример использования
$ mkdir C; touch 1.TXT a.TXT B.TXT C/D.TXT
$ find .
.
./C
./C/D.TXT
./1.TXT
./a.TXT
./B.TXT
$ find . -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
$ find .
.
./C
./C/D.txt
./a.txt
./B.txt
./1.txt
Объяснение
Вы найдете все файлы в текущем каталоге (.
) с периодом .
в его имени (-name '*.*'
) и запустите команду для каждого файла:
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "{}" "$a"
Эта команда означает: попробуйте преобразовать расширение файла в нижний регистр (что делает sed
):
$ echo 1.txt | sed -r "s/([^.]*)\$/\L\1/"
1.txt
$ echo 2.TXT | sed -r "s/([^.]*)\$/\L\1/"
2.txt
и сохраните результат в переменной a
.
Если что-то было изменено [ "$a" != "$0" ]
, переименуйте файл mv "$0" "$a"
.
Имя обрабатываемого файла ({}
) передано в sh -c
в качестве дополнительного аргумента, и оно отображается внутри командной строки как $0
.
Это делает script безопасным, так как в этом случае оболочка принимает {} как данные, а не как часть кода, как если она задана непосредственно в командной строке.
(Я благодарю @gniourf_gniourf за то, что указал на эту действительно важную проблему).
Как вы можете видеть, если вы используете {}
непосредственно в script,
возможно иметь
некоторые shell-инъекции в именах файлов, что-то вроде:
; rm -rf * ;
В этом случае инъекция будет рассматриваться оболочкой как часть
кода, и они будут выполнены.
While-версия
Более четкая, но немного более длинная версия script:
find . -name '*.*' | while IFS= read -r f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
Это все еще ломается для имен файлов, содержащих новые строки. Чтобы устранить эту проблему, вам необходимо иметь find
, который поддерживает -print0
(например, GNU find
) и Bash (так что read
поддерживает переключатель -d
):
find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
Это все еще ломается для файлов, которые содержат завершающие символы новой строки (так как они будут поглощены субблоком a=$(...)
. Если вам действительно нужен надежный метод (и вы должны!), с последней версией Bash (Bash ≥4.0), который поддерживает расширение параметра ,,
здесь окончательное решение:
find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
base=${f%.*}
ext=${f##*.}
a=$base.${ext,,}
[ "$a" != "$f" ] && mv -- "$f" "$a"
done
Вернуться к исходному решению
Или в одном find
go (обратно к исходному решению с некоторыми исправлениями, которые делают его действительно надежным):
find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
Я добавил -type f
, чтобы только обычные файлы были переименованы. Без этого у вас все еще могут быть проблемы, если имена каталогов переименованы перед именами файлов. Если вы также хотите переименовать каталоги (и ссылки, каналы и т.д.), Вы должны использовать -depth
:
find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
чтобы find
выполнял поиск по глубине.
Вы можете утверждать, что неэффективно порождать процесс bash
для каждого найденного файла. Это правильно, и предыдущая версия цикла была бы лучше.
Ответ 2
Я получил успех с этой командой.
rename JPG jpg *.JPG
Где rename
- это команда, которая сообщает оболочке переименовать каждое вхождение JPG
в JPG
в текущей папке со всеми именами файлов, имеющими расширение JPG
.
Если вы видите, что Bareword "JPG" не разрешен, а "строгие субтитры" используются в (1-й строке 1) линии 1 при таком подходе, попробуйте:
rename 's/\.JPG$/.jpg/' *.JPG
Ответ 3
Ну, вы можете использовать этот фрагмент в качестве основы любой альтернативы, в которой вы нуждаетесь:
#!/bin/bash
# lowerext.sh
while read f; do
if [[ "$f" = *.* ]]; then
# Extract the basename
b="${f%.*}"
# Extract the extension
x="${f##*.}"
# Convert the extension to lower case
# Note: this only works in recent versions of Bash
l="${x,,}"
if [[ "$x" != "$l" ]]; then
mv "$f" "$b.$l"
fi
else
continue
fi
done
Впоследствии все, что вам нужно сделать, это подать список файлов, которые нужно переименовать на стандартный ввод. Например. для всех файлов под текущим каталогом и любым подкаталогом:
find -type f | lowerext.sh
Небольшая оптимизация:
find -type f -name '*.*' | lowerext.sh
Вы должны быть более конкретными, если вам нужен более конкретный ответ, чем этот...
Ответ 4
Это более короткое, но более общее, в сочетании с другим ответом:
rename 's/\.([^.]+)$/.\L$1/' *
Моделирование
Для моделирования используйте -n
, т.е. rename -n 's/\.([^.]+)$/.\L$1/' *
. Таким образом, вы можете увидеть, что будет изменено до того, как будут выполнены реальные изменения. Пример вывода:
Happy.Family.GATHERING.JPG renamed as Happy.Family.GATHERING.jpg
Hero_from_The_Land_Across_the_River.JPG renamed as Hero_from_The_Land_Across_the_River.jpg
rAnD0m.jPg1 renamed as rAnD0m.jpg1
Краткое описание синтаксиса
- Синтаксис
rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
-
\.([^.]+)$
означает последовательность ничего, кроме точки ([^.]
) в конце строки ($
), после точки (\.
)
-
.\L$1
означает точку (\.
), за которой следует строчная (\L
) группа из 1 st ($1
)
- Первой группой в этом случае является расширение (
[^.]+
)
- Лучше использовать одиночную кавычку
'
вместо двойной кавычки "
, чтобы обернуть регулярное выражение, чтобы избежать расширения оболочки.
Ответ 5
Это выполнит задание для вашего .mp3 - но только в рабочем каталоге - однако может использовать имена файлов с пробелами:
for f in *.[mM][pP]3; do mv "$f" "${f%.*}.mp3"; done
Исправление:
for f in *.[mM][pP]3; do [[ "$f" =~ \.mp3$ ]] || mv "$f" "${f%.*}.mp3"; done
Ответ 6
Если у вас установлено mmv
(= перемещение нескольких файлов), а ваши имена файлов содержат не более одной точки, вы можете использовать
mmv -v "*.*" "#1.#l2"
Он не получает больше одной точки вправо (так как совпадение algo для *
не является жадным в mmv
), однако оно правильно обрабатывает ()
и '
. Пример:
$ mmv -v "*.*" "#1.#l2"
FOO.BAR.MP3 -> FOO.bar.mp3 : done
foo bar 'baz' (CD 1).MP3 -> foo bar 'baz' (CD 1).mp3 : done
Не идеальный, но гораздо более простой в использовании и запоминании, чем весь материал find/exec/sed
.
Ответ 7
рекурсивно для всего одного тонкого решения:
find -name '*.JPG' | sed 's/\(.*\)\.JPG/mv "\1.JPG" "\1.jpg"/' |sh
Вышеперечисленное рекурсивно переименовывает файлы с расширением "JPG" в файлы с расширением "jpg"
Ответ 8
Если вы используете ZSH:
zmv '(*).(*)' '$1.$2:l'
Если вы получите zsh: command not found: zmv
, просто запустите:
autoload -U zmv
И затем повторите попытку.
Благодаря этой оригинальной статье для подсказки о zmv и Документация ZSH для синтаксиса подстановки в нижнем регистре и в верхнем регистре.
Ответ 9
Итак, эти решения, которые выглядят как линейный шум, хороши и все, но это легко сделать из python REPL (я знаю, что OP запросил bash, но python установлен на множество систем, которые имеют bash в эти дни...):
import os
files = os.listdir('.')
for f in files:
path, ext = os.path.splitext(f)
if ext.isupper():
os.rename(f, path + ext.lower())
Ответ 10
Если вы заинтересованы только в некоторых расширениях файлов, таких как преобразование всех расширений более высокого размера "JPG" в нижний регистр "jpg", вы можете использовать утилиту командной строки переименовать. CD в каталог, который вы хотите изменить. Тогда
rename -n 's/\.JPG$/\.jpg/' *
Используйте параметр -n для проверки того, что будет изменено, а затем, когда вы довольны результатами, используйте без этого
rename 's/\.JPG$/\.jpg/' *