При попытке переименовать части нескольких имен файлов Rename возвращает «голое слово не разрешено».


12

У меня есть два файла в папке на моем Ubuntu 16.04:

a1.dat
b1.DAT

Я хочу переименовать, b1.DATчтобы b1.datв результате в папке были следующие файлы:

a1.dat
b1.dat

Я пытался (безуспешно):

$ rename *.DAT *.dat
Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

и

$ find . -iname "*.DAT" -exec rename DAT dat '{}' \;
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

Поиск этого не привел к значимому решению ...


Вы также можете узнать о mmv
PlasmaHH

Ответы:


14

Похоже, эта ошибка исходит от Perl rename. Вам нужно использовать цитаты, но вам нужно только указать часть, которую вы хотите изменить, стиль поиска и замены. Синтаксис такой:

rename -n 's/\.DAT/\.dat/' *

Удалить -nпосле тестирования, чтобы фактически переименовать файлы.

Чтобы включить скрытые файлы, измените параметр glob перед запуском команды:

shopt -s dotglob

Если вы хотите рекурсивно переименовывать файлы, вы можете использовать

shopt -s globstar
rename -n 's/\.DAT/\.dat/' **

или если в текущем каталоге или под ним находится много путей, которые не заканчиваются .DAT, лучше указать эти пути во второй команде:

rename -n 's/\.DAT/\.dat/' **/*.DAT

Это будет быстрее, если ваши файлы имеют разные имена, не заканчивающиеся на .DAT. [1]

Вы можете отключить эти настройки shopt -u, например shopt -u globstar, но они по умолчанию отключены и будут отключены при открытии новой оболочки.

Если это приводит к чрезмерно длинному списку аргументов, вы можете использовать, например find,:

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} \;

или лучше

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} +

Использование find ... -execwith +быстрее, чем использование, \;поскольку оно создает список аргументов из найденных файлов. Первоначально я думал, что вы не сможете использовать его, потому что вы упомянули, что у вас возникла argument list too longпроблема, но теперь я знаю, что список будет также хитро разбит на несколько вызовов команды по мере необходимости, чтобы избежать этой проблемы. [2] .

Поскольку renameвсе имена файлов будут обрабатываться одинаково, не имеет значения, насколько длинен список аргументов, поскольку его можно безопасно разделить на несколько вызовов. Если команда, с которой вы используете -exec, не принимает несколько аргументов или требует, чтобы ее аргументы были в определенном порядке, или по какой-либо другой причине разделение списка аргументов приведет к чему-то нежелательному, вы можете использовать его \;, что вызовет команду один раз. для каждого найденного файла (если список аргументов был слишком длинным для других методов, это займет много времени!).


Большое спасибо Элии Каган за очень полезные предложения по улучшению этого ответа:

[1] Указание имен файлов при подстановке .
[2] find ... -execс +разбивает список аргументов .


Спасибо. Как сделать это рекурсивно (для всех файлов во всех подпапках)?
Само

@ Само см. Мои правки :)
Занна

Не работает: $ rename 's / \. DAT / \. Dat /' ** bash: / usr / bin / rename: список аргументов слишком длинный
Само

1
@ Само вам нужно удалить -nиз переименования после тестирования - это просто чтобы показать, что будет изменено
Zanna

1
На Centos и Arch, переименование не проявляет такого поведения. Я получил здесь от использования Debian на WSL. Этот синтаксис работал.
кстианский

6

Ты можешь сделать:

rename -n 's/DAT$/\L$&/' *.DAT

Drop -nдля фактического переименования, чтобы иметь место.

  • шаблон glob *.DATсовпадает со всеми файлами .DATв текущем каталоге

  • в renameподстановке, DAT$совпадения DATв конце

  • \L$&делает весь матч в нижнем регистре; $&относится ко всему матчу

Если вы просто хотите сделать для b1.DAT:

rename -n 's/DAT$/\L$&/' b1.DAT

Пример:

% rename -n 's/DAT$/\L$&/' *.DAT
rename(b1.DAT, b1.dat)

4

Другие ответы касались одного из двух основных аспектов этого вопроса: как успешно выполнить операцию переименования, которая вам нужна. Цель этого ответа - объяснить, почему ваши команды не работают, в том числе значение этого странного сообщения об ошибке «bareword not selected» в контексте renameкоманды.

В первом разделе этого ответа речь идет об отношениях между renamePerl и о том, как renameиспользуется первый передаваемый им аргумент командной строки, который является аргументом кода. Второй раздел о том, как оболочка выполняет расширения - в частности, глобальные операции - для построения списка аргументов. В третьем разделе рассказывается о том, что происходит в коде Perl, который выдает ошибки «Bareword не разрешен». Наконец, четвертый раздел - это краткое изложение всех шагов, которые выполняются между вводом команды и получением ошибки.

1. Когда renameвы получаете странные сообщения об ошибках, добавьте «Perl» в ваш поиск.

В Debian и Ubuntu renameкоманда представляет собой скрипт Perl, который выполняет переименование файлов. В более старых выпусках - включая 14.04 LTS, которая все еще поддерживается на момент написания этой статьи - это была символическая ссылка, указывающая ( косвенно ) на prenameкоманду. В более новых выпусках он указывает вместо новой file-renameкоманды. Эти две команды переименования в Perl работают в основном одинаково, и я просто обращусь к ним обоим, как и renameк остальной части этого ответа.

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

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

Многие выражения в Perl неявно работают с $_переменной, если им не дано никакого другого выражения для использования в качестве операнда . Так , например, выражение подстановки $str =~ s/foo/bar/изменяет первое вхождение fooв строке , принадлежащих $str переменной к bar, или оставляет его неизменным , если она не содержит foo. Если вы просто написать s/foo/bar/без явного использования на =~оператора , то он работает на $_. Это сказать, что s/foo/bar/это мало для $_ =~ s/foo/bar/.

Распространено передать в s///выражение для в renameкачестве кода аргумента (т.е. первого аргумента командной строки), но вы не должны. Вы можете дать ему любой Perl-код, который вы хотите, чтобы он выполнялся внутри цикла, проверять каждое значение $_и (условно) изменять его.

Это имеет много интересных и полезных последствий , но подавляющее большинство из них выходит за рамки этого вопроса и ответа. Основная причина, по которой я привожу это здесь - фактически, главная причина, по которой я решил опубликовать этот ответ - состоит в том, что, поскольку первым аргументом renameявляется фактически код Perl, в любое время вы получаете странное сообщение об ошибке, и вы возникают проблемы с поиском информации об этом с помощью поиска, вы можете добавить «Perl» в строку поиска (или даже иногда заменить «переименовать» на «Perl»), и вы часто найдете ответ.

2. С rename *.DAT *.dat, то renameкоманда никогда не видела *.DAT!

Такая команда, как rename s/foo/bar/ *.txtправило, не передается программе *.txtв качестве аргумента командной строки rename, и вы этого не хотите , если только у вас нет файла с буквальным именем *.txt, чего, как мы надеемся, нет.

renameне интерпретирует образцы GLOB нравится *.txt, *.DAT, *.dat, x*, *y, или *когда ей передается в качестве аргумента имени пути. Вместо этого ваша оболочка выполняет расширение пути к ним (которое также называется расширением имени файла и также называется глобализацией). Это происходит до renameзапуска утилиты. Оболочка расширяет глобусы до потенциально нескольких имен путей и передает их всем в качестве отдельных аргументов командной строки rename. В Ubuntu ваша интерактивная оболочка - это Bash , если вы не изменили ее, поэтому я привел ссылку на справочное руководство Bash выше.

Существует одна ситуация, когда шаблон glob может быть передан как один нерасширенный аргумент командной строки rename: когда он не соответствует ни одному файлу. Разные оболочки демонстрируют разное поведение по умолчанию в этой ситуации, но поведение Bash по умолчанию состоит в том, чтобы просто передавать глобус буквально. Однако вы редко этого хотите! Если вы этого хотите, то убедитесь, что шаблон не раскрыт, заключив его в кавычки . Это касается передачи аргументов любой команде, а не только rename.

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

Код Perl s/foo/bar/не содержит ничего, специально обработанного оболочкой, но для меня было бы неплохо процитировать это тоже - и написать 's/foo/bar/'. (На самом деле, единственная причина , я не в том , что это будет сбивать с толку некоторых читателей, так как я еще не говорил о цитировании.) Поэтому я говорю , это было бы хорошо, потому что это очень распространено , что код Perl действительно содержит такие символы, и если бы я должен был изменить этот код, я мог бы не забыть проверить, нужно ли заключать в кавычки. Напротив, если вы хотите, чтобы оболочка расширяла глобус, она не должна заключаться в кавычки.

3. Что интерпретатор Perl подразумевает под «неиспользованным голым словом»

Сообщения об ошибках, которые вы показывали в своем вопросе, показывают, что при запуске rename *.DAT *.datваша оболочка расширялась *.DATдо списка из одного или нескольких имен файлов и что первое из этих имен было b1.DAT. Все последующие аргументы - как любые другие, развернутые из, так *.DATи любые расширения из *.dat--came после этого аргумента, поэтому они были бы интерпретированы как имена путей.

Потому что то, что на самом деле выполнялось, было чем-то похожим rename b1.DAT ..., и поскольку renameего первый b1.DATнеопциональный аргумент обрабатывается как код Perl, возникает вопрос: почему возникают ошибки «не разрешено использование слова», когда вы запускаете его как код Perl?

Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

В оболочке мы цитируем наши строки, чтобы защитить их от непреднамеренных расширений оболочки , которые в противном случае автоматически преобразовали бы их в другие строки (см. Раздел выше). Оболочки - это языки программирования специального назначения, которые работают совсем не так, как языки общего назначения (и их очень странный синтаксис и семантика отражают это). Но Perl - это язык программирования общего назначения, и, как и большинство языков программирования общего назначения, основной целью цитирования в Perl является не защита строк, а упоминание их вообще. Это на самом деле так, что большинство языков программирования похожи на естественный язык, В английском языке и предположим, что у вас есть собака, «ваша собака» - это фраза из двух слов, а ваша собака - собака. Точно так же в Perl '$foo'есть строка, а $fooесть то, чье имя $foo.

Однако, в отличие от почти любого другого языка программирования общего назначения, Perl также иногда интерпретирует текст без кавычек как упоминание строки - строки, которая является «такой же», как она, в том смысле, что она состоит из тех же символов в тот же порядок. Он будет пытаться интерпретировать код таким образом, только если это голое слово (нет $или другой символ, см. Ниже), и после этого он не сможет найти никакого другого значения, чтобы дать его. Тогда он будет восприниматься как строка, если вы не запретите это , включив ограничения .

Переменные в Perl обычно начинаются с символа пунктуации, называемого символом , который указывает широкий тип переменной. Например, $означает скаляр , @означает массив и %означает хэш . ( Есть и другие. ) Не беспокойтесь , если вы обнаружите , что в заблуждение (или скучно), потому что я только доведя его до сказать , что , когда действительное имя появляется в программе Perl , но не предшествует сигилы, что имя называется голым словом .

Голые слова служат различным целям, но они обычно означают встроенную функцию или пользовательскую подпрограмму , которая была определена в программе (или в модуле, используемом программой). Perl не имеет встроенных функций, вызываемых b1или DAT, поэтому, когда интерпретатор Perl видит код b1.DAT, он пытается обрабатывать b1и DATкак имена подпрограмм. Предполагая, что такие подпрограммы не были определены, это терпит неудачу. Затем, если ограничения не были включены, он обрабатывает их как строки. Это сработает, хотя кто-то догадывался , действительно ли вы собирались это сделать. .Оператор Perl объединяет строки , поэтому b1.DATвычисляет строкуb1DAT, То есть b1.DATэто плохой способ написать что-то вроде 'b1' . 'DAT'или "b1" . "DAT".

Вы можете проверить это самостоятельно, запустив команду perl -E 'say b1.DAT', которая передает короткий сценарий say b1.DATPerl интерпретатору Perl, который его запускает, печатая b1DAT. (В этой команде, в ' 'кавычки сказать оболочки пройти say b1.DATкак единый аргумент командной строки, в противном случае пространство будет вызывать sayи b1.DATбыть разобрано как отдельные слова , и perlбудут получать их в виде отдельных аргументов. perlНичего не видим кавычки сами, так как оболочка их убирает .)

Но теперь попробуйте написатьuse strict; в Perl-скрипте раньше say. Теперь происходит сбой с той же самой ошибкой, которую вы получили от rename:

$ perl -E 'use strict; say b1.DAT'
Bareword "b1" not allowed while "strict subs" in use at -e line 1.
Bareword "DAT" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.

Это произошло потому, что use strict;запретил интерпретатору Perl рассматривать голые слова как строки. Чтобы запретить эту особенность, было бы достаточно включить только subsограничение. Эта команда выдает те же ошибки, что и выше:

perl -E 'use strict "subs"; say b1.DAT'

Но обычно программисты на Perl просто пишут use strict;, что разрешает subsограничение и два других. use strict;как правило, рекомендуется практика. Таким образом, renameкоманда делает это для вашего кода. Вот почему вы получаете это сообщение об ошибке.

4. В итоге, вот что произошло:

  1. Ваша оболочка передана b1.DATв качестве первого аргумента командной строки, который renameобрабатывается как код Perl для запуска в цикле для каждого аргумента пути.
  2. Это было принято означать b1и DATсвязано с .оператором.
  3. b1и DATне были префиксом с символами, поэтому они рассматривались как голые слова.
  4. Эти два голых слова были бы приняты как имена встроенных функций или любых пользовательских подпрограмм, но ни у одного из этих имен не было таких вещей.
  5. Если бы «строгие подпрограммы» не были включены, то они были бы обработаны как строковые выражения 'b1'и 'DAT»и объединены. Это далеко от того, что вы намеревались, что показывает, как часто эта функция бесполезна.
  6. Но «строгие подлодки» была включена, потому что renameпозволяет все ограничения ( vars, refsи subs). Таким образом, вы получили ошибку вместо.
  7. renameвыйти из-за этой ошибки. Так как такого рода ошибки произошли рано, не было никаких попыток переименования файлов, даже если вы не прошли -n. Это хорошая вещь, которая часто защищает пользователей от непреднамеренных изменений имени файла и иногда даже от фактической потери данных.

Благодарю Занну , которая помогла мне устранить несколько важных недостатков в более удобном наброске этого ответа . Без нее этот ответ был бы менее понятным и, возможно, вообще не был бы опубликован.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.