Сценарий для удаления пробелов и нижнего регистра в именах файлов

Было бы легче использовать именованный канал для общения с процессами, чем попытка изменить FD, пока это открыто. Установите именованный канал как стандартный вход процесса и запишите в него как требуется.

7
29.03.2013, 15:58
5 ответов

На Debian и производных (включая Ubuntu), это легко с простым вызовом к rename:

$ touch 'A B'
$ rename 'tr/ A-Z/-a-z/' -- *
$ ls
a-b

Но Ваш rename другая утилита с тем же именем.

8
27.01.2020, 20:14
  • 1
    , о котором я полностью забыл, переименовывает команду :) –  T.P. 29.03.2013, 16:21
  • 2
    Это не работало на меня –  Joe 29.03.2013, 18:29
  • 3
    Вы, вероятно, используете, переименовывают от util-linux а не того, поставленного с жемчугом затем. –  Dennis Kaarsemaker 29.03.2013, 18:36
  • 4
    Обратите внимание, что это только перевело бы буквы ASCII, не другие буквы в текущей локали, если той локалью не является базирующийся ASCII. –  Stéphane Chazelas 30.03.2013, 01:18
  • 5
    Хм... Интересно, переименовывает ли жемчуг, принимает lc как аргумент. lc Perl сделает буквы неASCII. –  Dennis Kaarsemaker 30.03.2013, 01:19

Основная проблема состоит в том, что Вы не выполняете итерации по своим файлам: Вы выполняете итерации по единственной строке, "ls". Вы, вероятно, намеревались использовать обратные галочки вместо одинарных кавычек. Тем не менее, не анализировать ls.

Также необходимо заключить переменные в кавычки тем более, что Вы знаете, что они содержат пробел.

Только в ударе, можно сделать это:

declare -l lc    # whatever is stored in $lc is automatically lower cased
for x in *; do
    lc=${x//[[:space:]]/}  # replace all whitespace with nothing
    [ "$lc" != "$x" ] && mv -- "$x" "$lc"
done
3
27.01.2020, 20:14

Существует несколько проблем с Вашим сценарием.

for x in 'ls'

Вы выполняете итерации по списку, содержащему одно слово: ls. Вы, вероятно, означали писать `ls` (с обратными галочками), для парсинга вывода ls. Не анализируйте вывод ls: это не будет работать, если имена файлов будут содержать специальные символы, такие как пробелы. Оболочка уже имеет встроенный способ перечислить файлы в каталоге: подстановочные знаки. Так запись for x in *.

        if [ ! -f $x ]; then

Это не будет работать, если имя файла будет содержать пробел и другие специальные символы, потому что, когда Вы пишете $x вне кавычек результат разделяется на отдельные слова (и каждое слово интерпретируется как подстановочный шаблон, для начальной загрузки). Для предотвращения этого эффекта поместить $x в двойных кавычках: if [ ! -f "$x" ]; then. Можно помнить сложные правила или просто всегда использовать двойные кавычки вокруг подстановок переменных.

Несколько других строк повреждаются из-за отсутствия двойных кавычек. Просто поместите их вокруг каждого подстановки переменных.

       lc = `echo $x | tr '[A-Z]' '[a-z]'`

Это не будет работать из-за пробелов вокруг знака "равно": эта строка выполняется lc команда, передавая его = как его первый аргумент (и другие аргументы). Присвоение в оболочке не должно иметь никакого пробела вокруг = знак.

Вам не нужны скобки вокруг аргументов tr. Здесь команда, оказывается, работает, потому что Вы говорите для замены [ [ и ] ] в дополнение к печатанию строчными литерами.

Я рекомендую использовать долларовые круглые скобки $(…) вместо обратных галочек `…` для замены команды. Они эквивалентны, за исключением того, что обратные галочки имеют сложные правила когда дело доходит до заключения в кавычки вещей в нем, тогда как $(…) имеет очень интуитивный синтаксис. В целом, та команда должна быть: lc=$(echo "$x" | tr A-Z a-z)

find -name "* *" -type f | rename 's/ /-/g'

Ваше сообщение об ошибке показывает, что Вы имеете rename команда от пакета util-linux а не сценария Perl, найденного на Debian и производных (включая Ubuntu). Синтаксис, который Вы использовали только, имеет смысл со сценарием Perl.

Если Вы собираетесь использовать сценарий Perl, он может изменить случай на нижний регистр, таким образом, Вы не должны делать этого заранее. И Вы не должны звонить find если Вы не хотите переименовать файлы в подкаталогах также (который Ваша первая часть не обрабатывает). Если Вы хотите переименовать все файлы в текущем каталоге, можно просто записать:

prename 'tr/A-Z /a-z-/' -- *

Если Вы только хотите переименовать регулярные файлы (но не подкаталоги, символьные ссылки, и т.д.), использовать find:

find . -type f -maxdepth 1 -exec prename 'tr/A-Z /a-z-/' {} +

Если Вы хотите действовать на файлы в подкаталогах также, и имена каталогов могут содержать пробелы или прописные буквы, необходимо заботиться для оставления их в покое. Так как Вы находитесь на Linux, можно использовать -execdir звонить prename с аргументом, который не содержит имя каталога.

find . -type f -execdir prename 'tr/A-Z /a-z-/' {} +

Что, если у Вас нет Perl rename утилита? util-linux rename утилита не поможет Вам здесь. Можно поместить еще одну замену в цикл.

for x in ./*; do
  if [ -f "$x" ]; then
    y=$(echo "$x" | tr 'A-Z-' 'a-z ')
    mv "$x" "$y"
  fi
done

С ударом Вы не должны звонить tr, можно использовать его строковые конструкции замены.

for x in ./*; do
  if [ -f "$x" ]; then
    lc=${x,,}            # convert all letters to lowercase
    y=${lc// /-}         # replace spaces by hyphens
    if [ "$x" != "$y" ]; then
      mv "$x" "$y"
    fi
  fi
done

Если Вы хотите действовать на файлы в подкаталогах также, можно использовать рекурсивный шарик (включенный globstar опция). Снова, заботьтесь для оставления части каталога в покое, если они могут содержать прописные буквы или пробелы.

shopt -s globstar
for x in ./**/*; do
  if [ -f "$x" ]; then
    old_basename=${x##*/}
    new_basename=${old_basename,,}
    new_basename=${new_basename// /-}
    if [ "$new_basename" != "$old_basename" ]; then
      mv "${x%/*}/$old_basename" "${x%/*}/$new_basename"
    fi
  fi
done

В zsh, помещенном autoload -U zmv в Вашем .zshrc, и можно использовать zmv со спецификатором шарика . соответствовать только регулярным файлам и расширению параметра создает для переименования:

zmv -Q '*(.)' '${(L)f// /-}'

Действовать на файлы в подкаталогах также:

zmv -Qw '**/*(.)' '$1${(L)2// /-}'
6
27.01.2020, 20:14
  • 1
    С find -exec prename, Вы должны были бы только перевести basename, иначе это не будет работать правильно, если некоторые dirnames будут иметь символы верхнего регистра. Не все решения ведут себя то же для букв неASCII. –  Stéphane Chazelas 30.03.2013, 01:29
  • 2
    @StephaneChazelas Правда, я означал добавлять -maxdepth 1 поскольку исходный сценарий в вопросе был рекурсивным, но я забыл. Ответ достаточно длинен, как это, я придерживаюсь букв ASCII как вопрос. –  Gilles 'SO- stop being evil' 30.03.2013, 01:39
  • 3
    Также обратите внимание что традиционный SysV tr требуемый [...], таким образом, можно хотеть записать это tr '[A-Z]' '[a-z]' улучшить мобильность (как дополнительное [, ] не вредите для BSD/POSIX-style tr). –  Stéphane Chazelas 30.03.2013, 01:50

У Вас могли быть проблемы с командой "mv" и пробелами. Я окружил исходный файл двойными кавычками.

Этот сценарий работал на меня.

#!/bin/bash

for f in *
do
    g=$(echo $f | sed -e 's/\s/\-/g' | tr A-Z a-z)
    mv "$f" $g
done
2
27.01.2020, 20:14

В Bash 4$ {var} преобразовывает var в нижний регистр:

for f in *; do f2=${f,,}; mv "$f" "${f2// /-}"; done
2
27.01.2020, 20:14

Теги

Похожие вопросы