Beim Umbenennen wird "Barwort nicht zulässig" zurückgegeben, wenn versucht wird, Teile mehrerer Dateinamen in Kleinbuchstaben zu schreiben

12

Ich habe zwei Dateien in einem Ordner auf meinem Ubuntu 16.04:

a1.dat
b1.DAT

Ich möchte in umbenennen b1.DAT, b1.datdamit ich folgende Dateien im Ordner habe:

a1.dat
b1.dat

Ich habe es versucht (erfolglos):

$ 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).

und

$ 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).

Die Suche danach ergab keine sinnvolle Lösung ...

Samo
quelle
Vielleicht möchten Sie auch mehr über mmv
PlasmaHH

Antworten:

14

Dieser Fehler scheint von Perl zu stammen rename. Sie müssen Anführungszeichen verwenden, aber Sie müssen nur den Teil angeben, den Sie ändern möchten, den Stil suchen und ersetzen. Die Syntax lautet wie folgt:

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

-nNach dem Testen entfernen , um die Dateien tatsächlich umzubenennen.

Um versteckte Dateien einzuschließen, ändern Sie die Glob-Einstellung, bevor Sie den Befehl ausführen:

shopt -s dotglob

Wenn Sie Dateien rekursiv umbenennen möchten, können Sie verwenden

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

Wenn sich im oder unter dem aktuellen Verzeichnis viele Pfade befinden, die nicht mit enden .DAT, sollten Sie diese Pfade im zweiten Befehl angeben:

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

Dies ist schneller, wenn Ihre Dateien verschiedene Namen haben, die nicht auf enden .DAT. [1]

Zum Deaktivieren dieser Einstellungen können Sie shopt -uz. B. verwenden shopt -u globstar, diese sind jedoch standardmäßig deaktiviert und werden deaktiviert, wenn Sie eine neue Shell öffnen.

Wenn dies zu einer zu langen Argumentliste führt, können Sie beispielsweise Folgendes verwenden find:

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

oder besser

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

Die Verwendung find ... -execmit +ist schneller als die Verwendung, \;da aus den gefundenen Dateien eine Argumentliste erstellt wird. Ursprünglich dachte ich, dass Sie es nicht verwenden können, weil Sie erwähnt haben, dass Sie ein argument list too longProblem haben, aber jetzt weiß ich, dass die Liste bei Bedarf auch geschickt in mehrere Aufrufe des Befehls unterteilt wird, um dieses Problem zu vermeiden [2] .

Da renamejeder Dateiname auf dieselbe Weise verarbeitet wird, spielt es keine Rolle, wie lang die Argumentliste ist, da sie sicher auf mehrere Aufrufe aufgeteilt werden kann. Wenn der Befehl, mit dem Sie arbeiten, -execnicht mehrere Argumente akzeptiert oder erfordert, dass seine Argumente in einer bestimmten Reihenfolge vorliegen, oder wenn aus einem anderen Grund das Aufteilen der Argumentliste zu unerwünschten Ereignissen führt, können Sie \;den Befehl einmal aufrufen für jede gefundene Datei (wenn die Argumentliste für andere Methoden zu lang war, dauert dies lange!).


Vielen Dank an Eliah Kagan für die sehr nützlichen Vorschläge zur Verbesserung dieser Antwort:

[1] Angabe der Dateinamen beim Globbing .
[2] find ... -execmit +teilt die Argumentliste auf .

Zanna
quelle
Vielen Dank. Wie geht das rekursiv (für alle Dateien in allen Unterordnern)?
Samo
@ Samo siehe meine Bearbeitung :)
Zanna
Funktioniert nicht: $ umbenennen / \. DAT / \. Dat / '** bash: / usr / bin / umbenennen: Argumentliste zu lang
Samo
1
@ Samo müssen Sie die -nUmbenennung nach dem Testen entfernen - es ist nur zu zeigen, was geändert wird
Zanna
1
Bei Centos und Arch zeigt das Umbenennen dieses Verhalten nicht. Ich bin hierher gekommen, weil ich Debian für die WSL verwendet habe. Diese Syntax hat funktioniert.
xtian
6

Du kannst tun:

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

Drop -nfür die eigentliche Umbenennung.

  • Das Glob-Muster *.DATstimmt mit allen Dateien überein, .DATdie im aktuellen Verzeichnis enden

  • in der renameAuswechslung der DAT$Spiele DATam Ende

  • \L$&macht das ganze Match klein; $&bezieht sich auf das ganze Spiel

Wenn Sie nur tun möchten für b1.DAT:

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

Beispiel:

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

Andere Antworten haben einen der beiden Hauptaspekte dieser Frage angesprochen: die Frage, wie der von Ihnen benötigte Umbenennungsvorgang erfolgreich ausgeführt werden kann. Der Zweck dieser Antwort ist zu erklären, warum Ihre Befehle nicht funktionierten, einschließlich der Bedeutung dieser seltsamen Fehlermeldung "Bareword nicht erlaubt" im Kontext des renameBefehls.

Der erste Abschnitt dieser Antwort befasst sich mit der Beziehung zwischen renameund Perl und der renameVerwendung des ersten Befehlszeilenarguments, das Sie übergeben, nämlich des Codearguments. Im zweiten Abschnitt geht es darum, wie die Shell Erweiterungen - insbesondere Globbing - ausführt, um eine Argumentliste zu erstellen. Der dritte Abschnitt befasst sich mit den Vorgängen im Perl-Code, der Fehler "Bareword nicht erlaubt" enthält. Schließlich enthält der vierte Abschnitt eine Zusammenfassung aller Schritte, die zwischen der Eingabe des Befehls und dem Abrufen des Fehlers ausgeführt werden.

1. Wenn renameSie seltsame Fehlermeldungen erhalten, fügen Sie Ihrer Suche "Perl" hinzu.

In Debian und Ubuntu ist der renameBefehl ein Perl- Skript, das das Umbenennen von Dateien durchführt. Bei älteren Versionen - einschließlich 14.04 LTS, das zum jetzigen Zeitpunkt noch unterstützt wird - war dies ein symbolischer Link , der ( indirekt ) auf den prenameBefehl verweist . In neueren Versionen verweist es stattdessen auf den neueren file-renameBefehl. Diese beiden Perl-Umbenennungsbefehle funktionieren größtenteils auf die gleiche Weise, und ich beziehe mich nur auf beide, wie renamefür den Rest dieser Antwort.

Wenn Sie den renameBefehl verwenden, führen Sie nicht nur Perl-Code aus, den jemand anderes geschrieben hat. Sie schreiben auch Ihren eigenen Perl-Code und fordern ihn renameauf, ihn auszuführen. Dies liegt daran, dass das erste Befehlszeilenargument, das Sie an den renameBefehl übergeben, außer Optionsargumenten wie -n, aus tatsächlichem Perl-Code besteht . Der renameBefehl verwendet diesen Code, um jeden der Pfadnamen zu verarbeiten , die Sie als nachfolgende Befehlszeilenargumente übergeben. (Wenn Sie keine Pfadnamenargumente übergeben, werden stattdessen renamePfadnamen aus der Standardeingabe gelesen , einer pro Zeile.)

Der Code ist im Innern läuft eine Schleife , die iteriert einmal pro Pfad. Am Anfang jeder Iteration der Schleife wird der speziellen $_Variablen vor der Ausführung Ihres Codes der Pfadname zugewiesen, der gerade verarbeitet wird. Wenn Ihr Code bewirkt, dass der Wert von $_in etwas anderes geändert wird, wird diese Datei umbenannt, um den neuen Namen zu erhalten.

Viele Ausdrücke in Perl arbeiten implizit mit der $_Variablen, wenn ihnen kein anderer Ausdruck zur Verwendung als Operand zugewiesen wird . Zum Beispiel kann der Substitutionsausdruck $str =~ s/foo/bar/ändert das erste Auftreten fooin der Zeichenfolge durch den gehaltenen $str variable an bar, oder lässt sie unverändert , wenn es nicht enthält foo. Wenn Sie nur schreiben, s/foo/bar/ohne den =~Operator explizit zu verwenden , wird er ausgeführt $_. Das heißt, das s/foo/bar/ist die Abkürzung für $_ =~ s/foo/bar/.

Es ist üblich , passieren einen s///Ausdruck zu renameals Code Argument (dh der erste Befehlszeilenargument), aber Sie nicht müssen. Sie können ihm einen beliebigen Perl-Code geben, damit er innerhalb der Schleife ausgeführt wird, um jeden Wert zu untersuchen $_und (bedingt) zu ändern.

Dies hat viele coole und nützliche Konsequenzen , aber die überwiegende Mehrheit von ihnen geht weit über den Rahmen dieser Frage und Antwort hinaus. Der Hauptgrund, warum ich dies hier anspreche - in der Tat der Hauptgrund, warum ich mich dazu entschlossen habe, diese Antwort zu veröffentlichen - ist, darauf hinzuweisen, dass, da das erste Argument renametatsächlich Perl-Code ist, jedes Mal, wenn Sie eine seltsame Fehlermeldung erhalten und Sie Wenn Sie Probleme haben, Informationen zu finden, indem Sie suchen, können Sie der Suchzeichenfolge "Perl" hinzufügen (oder manchmal sogar "Umbenennen" durch "Perl" ersetzen), und Sie werden häufig eine Antwort finden.

2. Mit rename *.DAT *.dathat der renameBefehl nie gesehen *.DAT!

Ein Befehl wie wird rename s/foo/bar/ *.txtnormalerweise nicht *.txtals Befehlszeilenargument an das renameProgramm übergeben, und Sie möchten dies nicht, es sei denn, Sie haben eine Datei, deren Name buchstäblich lautet *.txt, was Sie hoffentlich nicht tun.

renamenicht interpretieren glob Muster mögen *.txt, *.DAT, *.dat, x*, *y, oder , *wenn sie es als Pfadname Argumente übergeben. Stattdessen führt Ihre Shell eine Pfadnamenerweiterung für sie durch (die auch als Dateinamenerweiterung und auch als Globbing bezeichnet wird). Dies geschieht, bevor das renameDienstprogramm ausgeführt wird. Die Shell erweitert Globs in potenziell mehrere Pfadnamen und übergibt sie alle als separate Befehlszeilenargumente an rename. In Ubuntu ist Ihre interaktive Shell Bash , es sei denn, Sie haben sie geändert. Deshalb habe ich oben auf das Bash-Referenzhandbuch verwiesen .

Es gibt eine Situation, in der ein Glob-Muster als einzelnes nicht erweitertes Befehlszeilenargument übergeben werden kann rename: Wenn es keinen Dateien entspricht. Unterschiedliche Shells weisen in dieser Situation ein unterschiedliches Standardverhalten auf, aber Bashs Standardverhalten besteht darin, den Glob einfach buchstäblich zu übergeben. Das wollen Sie aber selten! Wenn Sie es wollten, sollten Sie sicherstellen, dass das Muster nicht erweitert wird, indem Sie es zitieren . Dies gilt für die Übergabe von Argumenten an einen Befehl, nicht nur an rename.

Das Zitieren dient nicht nur zum Globbing (Dateinamenerweiterung), da Ihre Shell andere Erweiterungen für nicht zitierten Text und für einige von ihnen, aber nicht für andere , auch für in " "Anführungszeichen eingeschlossenen Text ausführt . Wenn Sie ein Argument übergeben möchten, das Zeichen enthält, die von der Shell speziell behandelt werden können, einschließlich Leerzeichen, sollten Sie es im Allgemeinen in Anführungszeichen setzen, vorzugsweise in ' 'Anführungszeichen .

Der Perl-Code s/foo/bar/enthält nichts, was speziell von der Shell behandelt wird, aber es wäre eine gute Idee für mich gewesen, das auch zu zitieren - und zu schreiben 's/foo/bar/'. (In der Tat, der einzige Grund , warum ich tat , war nicht , dass es für einige Leser wäre verwirrend, da ich über zitierte noch nicht gesprochen hatte.) Der Grund sagen , dass ich dies wäre gut gewesen ist , weil es sehr häufig ist , dass Perl - Code nicht enthalten Solche Zeichen, und wenn ich diesen Code ändern würde, könnte ich nicht daran denken, zu überprüfen, ob ein Zitat erforderlich ist. Wenn Sie dagegen möchten, dass die Shell einen Glob erweitert, darf dieser nicht in Anführungszeichen gesetzt werden.

3. Was der Perl-Interpreter unter "Barwort nicht erlaubt" versteht

Die Fehlermeldungen, die Sie in Ihrer Frage angezeigt haben, zeigen, dass rename *.DAT *.datIhre Shell beim Ausführen *.DATzu einer Liste mit einem oder mehreren Dateinamen erweitert wurde und dass der erste dieser Dateinamen war b1.DAT. Alle nachfolgenden Argumente - sowohl alle anderen *.DATals auch alle erweiterten Argumente - *.datkamen nach diesem Argument, sodass sie als Pfadnamen interpretiert worden wären.

Da das, was tatsächlich ausgeführt wurde, ungefähr so rename b1.DAT ...war und renamedas erste Argument ohne Option als Perl-Code behandelt wird, stellt sich die Frage: Warum werden b1.DATdiese Fehler "Bareword nicht zulässig" erzeugt, wenn Sie es als Perl-Code ausführen?

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).

In einer Shell zitieren wir unsere Zeichenfolgen , um sie vor unbeabsichtigten Shell-Erweiterungen zu schützen, die sie sonst automatisch in andere Zeichenfolgen umwandeln würden (siehe Abschnitt oben). Shells sind spezielle Programmiersprachen, die ganz anders funktionieren als Allzwecksprachen (und ihre sehr seltsame Syntax und Semantik spiegeln dies wider). Perl ist jedoch eine universelle Programmiersprache, und wie die meisten universellen Programmiersprachen besteht der Hauptzweck des Zitierens in Perl nicht darin , Zeichenfolgen zu schützen , sondern sie überhaupt zu erwähnen. Auf diese Weise ähneln die meisten Programmiersprachen der natürlichen Sprache. Auf Englisch und wenn Sie einen Hund haben, ist "Ihr Hund" eine Zwei-Wort-Phrase, während Ihr Hund ein Hund ist. In ähnlicher Weise ist in Perl '$foo'eine Zeichenfolge, während $fooes sich um etwas handelt, dessen Name lautet $foo.

Doch im Gegensatz zu fast all anderen Allzweck-Programmiersprache, wird Perl auch manchmal nicht notierten Text interpretieren als erwähnen einen String - eine Zeichenfolge , die die „gleiche“ wie es ist, in dem Sinne , dass es in den gleichen Zeichen aufgebaut ist die gleiche Reihenfolge. Es wird nur dann versucht, Code so zu interpretieren, wenn es sich um ein Barwort handelt (kein $oder ein anderes Siegel, siehe unten) und nachdem es keine andere Bedeutung gefunden hat, um es zu geben. Dann wird es als Zeichenfolge verwendet, es sei denn, Sie weisen es an, indem Sie Einschränkungen aktivieren .

Variablen in Perl beginnen normalerweise mit einem Interpunktionszeichen, das als Siegel bezeichnet wird und den allgemeinen Typ der Variablen angibt. Zum Beispiel $bedeutet Skalar , @Mittel Array und %mittels Hash . ( Es gibt andere. ) Machen Sie sich keine Sorgen, wenn Sie dies verwirrend (oder langweilig) finden, denn ich spreche es nur an, um zu sagen, dass, wenn ein gültiger Name in einem Perl-Programm erscheint, aber kein Siegel vorangestellt ist, dieser Name soll ein Barwort sein .

Barwörter dienen verschiedenen Zwecken, bezeichnen jedoch normalerweise eine integrierte Funktion oder eine benutzerdefinierte Unterroutine , die im Programm (oder in einem vom Programm verwendeten Modul) definiert wurde. Perl hat keine Funktionen in integrierten genannt b1oder DAT, also , wenn der Perl - Interpreter den Code sieht b1.DAT, versucht sie zu behandeln b1und DATwie die Namen von Subroutinen. Vorausgesetzt, es wurden keine solchen Unterprogramme definiert, schlägt dies fehl. Sofern Einschränkungen nicht aktiviert wurden, werden sie als Zeichenfolgen behandelt. Dies wird funktionieren, aber ob Sie dies tatsächlich beabsichtigt haben oder nicht , ist unklar. Der Perl- .Operator verkettet Zeichenfolgen und b1.DATwertet sie daher als Zeichenfolge ausb1DAT. Das heißt, b1.DATist ein schlechter Weg, um so etwas wie 'b1' . 'DAT'oder zu schreiben "b1" . "DAT".

Sie können dies selbst testen, indem Sie den Befehl ausführen perl -E 'say b1.DAT', der das kurze Perl-Skript say b1.DATan den Perl-Interpreter übergibt , der es druckt b1DAT. (In diesem Befehl wird die ' 'Anführungszeichen sagen dem Shell übergeben say b1.DATals ein einziges Befehlszeilenargument, andernfalls würde der Raum verursacht sayund b1.DATwird als getrennte Worte analysiert und perlsich als separate Argumente erhalten würde. perlHat nicht die Anführungszeichen selbst sehen, wie die Shell entfernt sie .)

Versuchen Sieuse strict; jetzt, vorher im Perl-Skript zu schreibensay . Jetzt schlägt es mit der gleichen Art von Fehler fehl, die Sie erhalten haben 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.

Dies geschah, weil use strict;der Perl-Interpreter Barewords nicht als Zeichenfolgen behandeln durfte. Um dieses spezielle Merkmal zu verbieten, würde es wirklich ausreichen, nur die subsEinschränkung zu aktivieren . Dieser Befehl erzeugt die gleichen Fehler wie oben:

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

Aber normalerweise schreiben Perl-Programmierer nur use strict;, was die subsEinschränkung und zwei andere ermöglicht. use strict;wird allgemein empfohlen. Der renameBefehl erledigt dies also für Ihren Code. Deshalb erhalten Sie diese Fehlermeldung.

4. Zusammenfassend ist Folgendes passiert:

  1. Ihre Shell wurde b1.DATals erstes Befehlszeilenargument übergeben, das renameals Perl-Code behandelt wurde , der für jedes Pfadnamenargument in einer Schleife ausgeführt wird.
  2. Dies wurde so verstanden , b1und DATmit dem angeschlossenen .Betreiber.
  3. b1und DATwurden nicht mit Siegeln vorangestellt, so dass sie als Barwörter behandelt wurden.
  4. Diese beiden Barwörter wären als Namen von integrierten Funktionen oder benutzerdefinierten Unterprogrammen verwendet worden, aber keiner dieser Namen hatte einen dieser Namen.
  5. Wenn "strenge Subs" nicht aktiviert worden wären, wären sie wie die Zeichenfolgenausdrücke 'b1'und 'DAT'behandelt und verkettet worden. Das ist weit von dem entfernt, was Sie beabsichtigt haben, was zeigt, dass diese Funktion oft nicht hilfreich ist.
  6. Aber „strict subs“ wurde aktiviert, weil renamealle ermöglicht Einschränkungen ( vars, refs, und subs). Daher haben Sie stattdessen einen Fehler erhalten.
  7. renameBeenden Sie aufgrund dieses Fehlers. Da diese Art von Fehler früh auftrat, wurden keine Versuche zum Umbenennen von Dateien unternommen, obwohl Sie nicht bestanden hatten -n. Dies ist eine gute Sache, die Benutzer häufig vor unbeabsichtigten Dateinamenänderungen und gelegentlich sogar vor tatsächlichem Datenverlust schützt.

Vielen Dank an Zanna , die mir geholfen hat, einige wichtige Mängel in einem besseren Entwurf dieser Antwort zu beheben . Ohne sie hätte diese Antwort viel weniger Sinn ergeben und könnte überhaupt nicht veröffentlicht werden.

Eliah Kagan
quelle