Перейменовуючи, повертає "барево не дозволено" при спробі зменшити регістри кількох імен файлів


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
ПлазмаHH

Відповіді:


14

Ця помилка виглядає так, що вона походить від Perl rename. Вам потрібно використовувати цитування, але вам потрібно лише вказати частину, яку ви хочете змінити, шукати та замінювати стиль. Синтаксис такий:

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

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

Щоб включити приховані файли, змініть налаштування глобула перед запуском команди:

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 ... -execз +швидше, ніж використовувати, \;оскільки він створює список аргументів із знайдених файлів. Спочатку я думав, що не можна використовувати його, тому що ви згадали, що у вас виникає argument list too longпроблема, але тепер я знаю, що список також буде розумно розбитий на кілька викликів команди, як це потрібно, щоб уникнути цієї проблеми [2] .

Оскільки renameкожне ім’я файлу буде оброблятися однаково, не має значення, скільки триває список аргументів, оскільки його можна безпечно розділити на кілька викликів. Якщо команда, з якою ви використовуєте -exec, не приймає декілька аргументів або вимагає, щоб її аргументи були в певному порядку або з будь-якої іншої причини розділення списку аргументів призведе до того, що трапиться щось небажане, ви можете скористатися \;, через що команду буде викликано один раз для кожного знайденого файлу (якщо список аргументів був занадто довгим для інших методів, це займе багато часу!).


Велике спасибі Еліа Каган за дуже корисні пропозиції щодо покращення цієї відповіді:

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


Дякую. Як це зробити рекурсивно (для всіх файлів у всіх папках)?
Само

@Samo дивись мою редакцію :)
Zanna

Не працює: $ перейменувати 's / \. DAT / \. Dat /' ** bash: / usr / bin / rename: Список аргументів занадто довгий
Само

1
@Samo -nпісля тестування потрібно зняти назву з перейменування - це просто показати, що буде змінено
Zanna

1
У Centos і Arch перейменування не проявляє такої поведінки. Я потрапив сюди за допомогою Debian на WSL. Цей синтаксис спрацював.
xtian

6

Ви можете зробити:

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

Перехід -nдо фактичного перейменування має відбутися.

  • шаблон шаблону *.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 allowed" у контексті 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 .

Існує одна ситуація, коли глобальний шаблон може бути переданий як єдиний аргумент командного рядка rename: коли він не відповідає жодним файлам. У різних ситуаціях оболонки проявляють різну поведінку за замовчуванням у цій ситуації, але поведінка за замовчуванням Баша полягає у тому, щоб просто пройти глобальну точку буквально. Однак ти рідко хочеш цього! Якщо ви цього хотіли, то слід переконатися, що модель не розширюється, цитуючи її. Це стосується передачі аргументів будь-якій команді, а не лише до rename.

Цитування не тільки для глобулювання (розширення назви файлів), оскільки є інші розширення, які ваша оболонка виконує на тексті без котирування, а для деяких з них, але не на інших , також на текст, укладений у " "лапки. Загалом, у будь-який час, коли ви хочете передати аргумент, який містить символи, які можуть бути оброблені спеціально оболонкою, включаючи пробіли, вам слід навести її, бажано з ' 'цитатами .

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

3. Що означає перекладач Perl, "барево не дозволено"

Повідомлення про помилки, які ви показали у своєму запитанні, показують, що, коли ви бігли rename *.DAT *.dat, ваша оболонка розширилася *.DATдо списку однієї або декількох імен і що перша з цих імен була b1.DAT. Всі наступні аргументи - і будь-які інші, розширені з, *.DATі будь-які розширені з *.dat--були після цього аргументу, тому вони будуть інтерпретовані як імена шляхів.

Оскільки те, що насправді було запущено, було щось подібне rename b1.DAT ..., і тому, що renameрозглядає свій перший аргумент, що не є варіантом, як код Perl, виникає питання: чому ж b1.DATвиникають ці помилки "не дозволяється", коли ви запускаєте його як код 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 зазвичай починаються з пунктуаційного символу під назвою sigil , який визначає широкий тип змінної. Наприклад, $означає скаляр , @означає масив і %означає хеш . ( Є й інші. ) Не хвилюйтесь, якщо ви вважаєте це заплутаним (або нудним), тому що я лише доводжу це, щоб сказати, що коли дійсне ім'я з'являється в програмі Perl, але йому не передує sigil, це ім'я кажуть, що це голове слово .

Бареври служать різним цілям, але вони зазвичай означають вбудовану функцію або визначену користувачем підпрограму , визначену в програмі (або в модулі, який використовується програмою). 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.