Zmiana nazwy zwraca „słowo zabronione niedozwolone” podczas próby zapisania małych części wielu nazw plików

12

Mam dwa pliki w folderze na moim Ubuntu 16.04:

a1.dat
b1.DAT

Chcę zmienić nazwę, b1.DATaby b1.datw folderze były następujące pliki:

a1.dat
b1.dat

Próbowałem (bezskutecznie):

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

i

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

Poszukiwanie tego nie dało żadnego sensownego rozwiązania ...

Samo
źródło
Możesz także chcieć dowiedzieć się o mmv
PlasmaHH 17.03.18

Odpowiedzi:

14

Ten błąd wygląda tak, jakby pochodzi od Perla rename. Musisz użyć cudzysłowu, ale musisz tylko określić część, którą chcesz zmienić, wyszukać i zamienić styl. Składnia jest następująca:

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

Usuń -npo przetestowaniu, aby faktycznie zmienić nazwę plików.

Aby dołączyć ukryte pliki, zmień ustawienia globalne przed uruchomieniem polecenia:

shopt -s dotglob

Jeśli chcesz rekursywnie zmieniać nazwy plików, możesz użyć

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

lub jeśli w bieżącym katalogu znajduje się wiele ścieżek, które się nie kończą .DAT, lepiej podać te ścieżki w drugim poleceniu:

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

Będzie to szybsze, jeśli twoje pliki mają różne nazwy, które nie kończą się na .DAT. [1]

Aby wyłączyć te ustawienia, możesz użyć shopt -unp. shopt -u globstarSą one jednak domyślnie wyłączone i będą wyłączone po otwarciu nowej powłoki.

Jeśli powoduje to zbyt długą listę argumentów, możesz na przykład użyć find:

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

albo lepiej

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

Używanie find ... -execz +jest szybsze niż używanie, \;ponieważ tworzy listę argumentów ze znalezionych plików. Początkowo myślałem, że nie będzie można go użyć, ponieważ wspomniałeś, że masz argument list too longproblem, ale teraz wiem, że lista będzie również sprytnie podzielona na wiele wywołań polecenia w razie potrzeby, aby uniknąć tego problemu [2] .

Ponieważ renamekażda nazwa pliku będzie przetwarzana w ten sam sposób, nie ma znaczenia, jak długo trwa lista argumentów, ponieważ można ją bezpiecznie podzielić na wiele wywołań. Jeśli polecenie, którego używasz -exec, nie akceptuje wielu argumentów lub wymaga, aby jego argumenty były w określonej kolejności lub z jakiegokolwiek innego powodu podzielenie listy argumentów spowoduje, że wydarzy się coś niepożądanego, możesz użyć \;, co spowoduje, że polecenie zostanie wywołane raz dla każdego znalezionego pliku (jeśli lista argumentów była zbyt długa dla innych metod, zajmie to dużo czasu!).


Ogromne podziękowania dla Eliasza Kagana za bardzo przydatne sugestie w celu poprawy tej odpowiedzi:

[1] Określanie nazw plików podczas globowania .
[2] find ... -execz +dzieli listę argumentów .

Zanna
źródło
Dzięki. Jak to zrobić rekurencyjnie (dla wszystkich plików we wszystkich podfolderach)?
Samo,
@Samo zobacz moją edycję :)
Zanna
Nie działa: $ rename 's / \. DAT / \. Dat /' ** bash: / usr / bin / rename: Zbyt długa lista argumentów
Samo
1
@Samo musisz usunąć -nnazwę po zmianie po przetestowaniu - to tylko, aby pokazać, co zostanie zmienione
Zanna
1
W Centos i Arch zmiana nazwy nie wykazuje tego zachowania. Przybyłem tutaj z używania Debiana na WSL. Ta składnia działała.
xtian
6

Możesz to zrobić:

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

Upuść, -naby nastąpiła faktyczna zmiana nazwy.

  • wzorzec glob *.DATpasuje do wszystkich plików, które kończą się w .DATbieżącym katalogu

  • w renamepodstawieniu, DAT$mecze DATna końcu

  • \L$&sprawia, że ​​całe dopasowanie jest małe; $&odnosi się do całego meczu

Jeśli chcesz zrobić tylko dla b1.DAT:

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

Przykład:

% rename -n 's/DAT$/\L$&/' *.DAT
rename(b1.DAT, b1.dat)
heemayl
źródło
4

Inne odpowiedzi dotyczyły jednego z dwóch głównych aspektów tego pytania: tego, jak skutecznie przeprowadzić wymaganą operację zmiany nazwy. Celem tej odpowiedzi jest wyjaśnienie, dlaczego Twoje polecenia nie działały, w tym znaczenie tego dziwnego komunikatu o błędzie „niedozwolone słowo” w kontekście renamepolecenia.

Pierwsza część tej odpowiedzi dotyczy relacji między renamePerlem a tym, w jaki sposób renameużywasz pierwszego przekazanego argumentu wiersza poleceń, który jest argumentem kodu. Druga sekcja dotyczy tego, jak powłoka wykonuje rozszerzenia - w szczególności globowanie - w celu utworzenia listy argumentów. Trzecia sekcja dotyczy tego, co dzieje się w kodzie Perla, w którym występują błędy „Bareword niedozwolone”. Wreszcie czwarta sekcja zawiera podsumowanie wszystkich kroków, które mają miejsce między wprowadzeniem polecenia a otrzymaniem błędu.

1. Gdy renamepojawi się dziwny komunikat o błędzie, dodaj „Perl” do wyszukiwania.

W Debianie i Ubuntu renamepolecenie jest skryptem Perla, który dokonuje zmiany nazwy pliku. W starszych wersjach - w tym 14.04 LTS, które nadal są obsługiwane w chwili pisania tego tekstu - było to dowiązanie symboliczne ( pośrednio ) do prenamepolecenia. W nowszych wersjach wskazuje zamiast tego na nowszą file-renamekomendę. Te dwa polecenia zmiany nazwy Perla działają głównie w ten sam sposób, a ja po prostu odwołam się do nich obu, tak jak renamew pozostałej części tej odpowiedzi.

Kiedy używasz renamepolecenia, nie tylko uruchamiasz kod Perla, który napisał ktoś inny. Piszesz także własny kod Perla i każesz renamego uruchomić. To dlatego, że pierwszy argument wiersza polecenia można przejść do renamepolecenia, inne niż argumentów opcjonalnych , takich jak -n, składa się z rzeczywistego kodu Perl . renameKomenda używa tego kodu do działania na każdej z ścieżek , aby przekazać go jako kolejne argumenty wiersza polecenia. (Jeśli nie podasz żadnych argumentów renamenazw ścieżek, zamiast tego odczytuje nazwy ścieżek ze standardowego wejścia , po jednym w wierszu).

Kod jest uruchamiany w pętli , która iteruje raz na ścieżkę. U góry każdej iteracji pętli, przed uruchomieniem kodu, specjalnej $_zmiennej przypisana jest aktualnie przetwarzana nazwa ścieżki. Jeśli kod powoduje zmianę wartości $_na coś innego, wówczas nazwa tego pliku zostaje zmieniona na nową.

Wiele wyrażeń w Perlu działa niejawnie na $_zmiennej, gdy nie otrzymuje żadnego innego wyrażenia do użycia jako operand . Na przykład, wyrażenie podstawienie $str =~ s/foo/bar/zmienia pierwsze wystąpienie foow ciągu posiadanych przez $str zmienną do barlub pozostawia bez zmian, jeśli nie zawierają foo. Jeśli po prostu pisać s/foo/bar/bez wyraźnego korzystania z =~operatorem , to działa na $_. To znaczy s/foo/bar/krótko $_ =~ s/foo/bar/.

Często zdarza się, że s///wyrażenie jest renameargumentem kodu (tj. Pierwszym argumentem wiersza poleceń), ale nie musisz. Możesz podać dowolny kod Perla, który ma być uruchamiany w pętli, aby sprawdzić każdą wartość $_i (warunkowo) zmodyfikować go.

Ma to wiele fajnych i przydatnych konsekwencji , ale ogromna większość z nich znacznie wykracza poza zakres tego pytania i odpowiedzi. Głównym powodem, dla którego to tu przytaczam - w rzeczywistości głównym powodem, dla którego postanowiłem opublikować tę odpowiedź - jest wskazanie, że ponieważ pierwszym argumentem renamejest kod Perla, za każdym razem, gdy pojawia się dziwny komunikat o błędzie, a ty masz problem ze znalezieniem informacji na ten temat podczas wyszukiwania, możesz dodać „Perl” do ciągu wyszukiwania (lub nawet zastąpić „zmień nazwę” na „Perl”, czasami) i często znajdziesz odpowiedź.

2. rename *.DAT *.datThe renamekomenda nie widziałem *.DAT!

Polecenie jak rename s/foo/bar/ *.txtzwykle nie przechodzą *.txtjako argument wiersza polecenia do renameprogramu, a nie chcesz go , chyba że masz plik, którego nazwa jest dosłownie *.txt, co mam nadzieję, że nie.

renamenie interpretuje wzorce glob podoba *.txt, *.DAT, *.dat, x*, *y, lub *, gdy przeszedł do niego jako argumenty ścieżki dostępu. Zamiast tego, twoja powłoka wykonuje na nich interpretację nazw ścieżek (co jest również nazywane rozszerzaniem nazw plików, a także nazywane globowaniem). Dzieje się tak przed uruchomieniem renamenarzędzia. Powłoka rozwija globusy do potencjalnie wielu nazw ścieżek i przekazuje je wszystkie, jako osobne argumenty wiersza poleceń, do rename. W Ubuntu twoją interaktywną powłoką jest Bash , chyba że ją zmieniłeś, dlatego podłączyłem się do powyższej instrukcji obsługi Bash .

Istnieje jedna sytuacja, w której wzorzec globalny może zostać przekazany jako pojedynczy nierozwinięty argument wiersza poleceń do rename: gdy nie pasuje on do żadnego pliku. Różne powłoki wykazują różne domyślne zachowanie w tej sytuacji, ale domyślnym zachowaniem Basha jest po prostu przekazanie globu dosłownie. Jednak rzadko tego chcesz! Jeśli chcesz, powinieneś upewnić się, że wzorzec nie zostanie rozwinięty, cytując go. Dotyczy to przekazywania argumentów do dowolnego polecenia, a nie tylko do rename.

Cytowanie nie służy tylko do globowania (rozwijania nazw plików), ponieważ istnieją inne rozszerzenia, które twoja powłoka wykonuje na cudzysłowach tekstowych, a dla niektórych z nich, ale nie dla innych , również na tekstach zawartych w " "cudzysłowach. Ogólnie rzecz biorąc, za każdym razem, gdy chcesz przekazać argument zawierający znaki, które mogą być traktowane specjalnie przez powłokę, w tym spacje, powinieneś ją zacytować, najlepiej z ' 'cudzysłowami .

Kod Perla s/foo/bar/nie zawiera niczego specjalnie potraktowanego przez powłokę, ale dobrze byłoby, żebym to zacytował - i napisał 's/foo/bar/'. (W rzeczywistości, jedynym powodem, dla którego nie było, że to być mylące dla niektórych czytelników, a ja jeszcze nie rozmawialiśmy o cytowanie). Dlatego mówię to byłoby dobre, ponieważ jest to bardzo powszechne, że kod Perl ma zawierać takich znaków, a jeśli miałbym zmienić ten kod, mógłbym nie pamiętać, aby sprawdzić, czy cytowanie jest potrzebne. Natomiast jeśli chcesz, aby powłoka rozszerzyła glob, nie można jej cytować.

3. Co interpreter języka Perl rozumie przez „słowo zabronione niedozwolone”

Komunikaty o błędach, które pokazałeś w swoim pytaniu, ujawniają, że po uruchomieniu rename *.DAT *.dattwoja powłoka rozwinęła *.DATsię do listy jednego lub więcej nazw plików i że pierwsza z tych nazw była b1.DAT. Wszystkie kolejne argumenty - zarówno inne rozwinięte z, jak *.DATi wszelkie rozwinięte z - *.datpojawiły się po tym argumencie, więc zostałyby zinterpretowane jako nazwy ścieżek.

Ponieważ to, co faktycznie działało, było czymś podobnym rename b1.DAT ...i ponieważ renametraktuje swój pierwszy b1.DATnieopcyjny argument jako kod Perla, powstaje pytanie: dlaczego produkuje te błędy „niedozwolone słowo”, gdy uruchamiasz go jako kod Perla?

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

W powłoce cytujemy nasze łańcuchy, aby chronić je przed niezamierzonymi rozszerzeniami powłoki , które w przeciwnym razie przekształciłyby je automatycznie w inne łańcuchy (patrz sekcja powyżej). Powłoki są językami programowania specjalnego przeznaczenia, które działają zupełnie inaczej niż języki ogólnego przeznaczenia ( odzwierciedlają to ich bardzo dziwna składnia i semantyka ). Ale Perl jest językiem programowania ogólnego przeznaczenia i, podobnie jak większość języków programowania ogólnego przeznaczenia, głównym celem cytowania w Perlu nie jest ochrona ciągów, ale w ogóle o nich mowa. W ten sposób większość języków programowania jest podobnych do języka naturalnego. W języku angielskim i przypuśćmy, że masz psa, „twój pies” jest dwusłowowym zwrotem, podczas gdy twój pies jest psem. Podobnie, w Perlu, '$foo'jest łańcuchem, podczas gdy $foojest czymś o nazwie $foo.

Jednak w przeciwieństwie do niemal każdego innego języka programowania ogólnego przeznaczenia, Perl będzie również czasem zinterpretować tekst niecytowany jak wymienić łańcuch - ciąg znaków, który jest „taki sam”, jak to, w tym sensie, że składa się z tych samych znaków to samo zamówienie. Spróbuje on zinterpretować kod w ten sposób tylko wtedy, gdy będzie to słowo kluczowe (brak $lub inny znak, patrz poniżej), a po tym nie będzie mógł znaleźć innego znaczenia dla nadania go. Następnie weźmie to jako ciąg, chyba że powiesz, aby nie włączać ograniczeń .

Zmienne w Perlu zwykle zaczynają się od znaku interpunkcyjnego zwanego sigil , który określa szeroki typ zmiennej. Na przykład $oznacza skalar , @oznacza tablicę i %oznacza skrót . ( Są inne. ) Nie martw się, jeśli uznasz to za mylące (lub nudne), ponieważ podnoszę to tylko po to, by powiedzieć, że kiedy poprawna nazwa pojawia się w programie Perla, ale nie jest poprzedzona sigilem, ta nazwa jest uważane za jedno słowo .

Barewords służą różnym celom, ale zwykle oznaczają wbudowaną funkcję lub podprogram zdefiniowany przez użytkownika , który został zdefiniowany w programie (lub w module używanym przez program). Perl nie ma wywoływanych wbudowanych funkcji b1lub DAT, więc gdy interpreter Perla zobaczy kod b1.DAT, próbuje traktować b1i DATjak nazwy podprogramów. Zakładając, że nie zdefiniowano takich podprogramów, to się nie powiedzie. Następnie, pod warunkiem że ograniczenia nie zostały włączone, traktuje je jak łańcuchy. To zadziała, ale czy ktoś tak naprawdę chciał, aby tak się stało, nikt nie zgadnie. .Operator Perla konkatenuje łańcuchy , więc b1.DATocenia łańcuchb1DAT. To jest b1.DATzły sposób na napisanie czegoś takiego jak 'b1' . 'DAT'lub "b1" . "DAT".

Możesz to sprawdzić sam, uruchamiając polecenie perl -E 'say b1.DAT', które przekazuje krótki skrypt Perla say b1.DATdo interpretera Perla, który go uruchamia, drukując b1DAT. (W tym poleceniu ' 'cytaty powiedzieć skorupę przekazać say b1.DATjako pojedynczy argument wiersza polecenia, w przeciwnym razie przestrzeń spowodowałoby sayi b1.DATbyć analizowany jako oddzielne słów i perlbędzie odbierać je jako osobne argumenty. perlNie nie patrz cytaty same, jak powłoka je usuwa ).

Ale teraz spróbuj pisać wcześniejuse strict; w skrypcie Perl say. Teraz kończy się niepowodzeniem z tego samego rodzaju błędem, który otrzymałeś 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.

Stało się tak, ponieważ use strict;zabronił interpreterowi Perla traktowania słów bez wyrazu jako ciągów znaków. Aby zakazać tej konkretnej funkcji, naprawdę wystarczy włączyć tylko subsograniczenie. To polecenie powoduje takie same błędy jak powyżej:

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

Ale zwykle programiści Perla po prostu piszą use strict;, co umożliwia subsograniczenie i dwóch innych. use strict;jest ogólnie zalecaną praktyką. Więc renamepolecenie robi to dla twojego kodu. Dlatego pojawia się ten komunikat o błędzie.

4. Podsumowując, tak się stało:

  1. Powłoka została przekazana b1.DATjako pierwszy argument wiersza poleceń, który renametraktował jako kod Perla, aby działał w pętli dla każdego argumentu nazwy ścieżki.
  2. To miało oznaczać b1i DATzwiązane z .operatorem.
  3. b1i DATnie były poprzedzone pieczęciami, dlatego traktowano je jak słowa bez słów.
  4. Te dwa słowa byłyby nazwane funkcjami wbudowanymi lub dowolnymi podprogramami zdefiniowanymi przez użytkownika, ale żadna z tych rzeczy nie miała żadnej z tych nazw.
  5. Gdyby nie włączono „ścisłych napisów”, byłyby one traktowane jak wyrażenia łańcuchowe 'b1'i 'DAT„i łączone”. To dalekie od zamierzonego, co pokazuje, że ta funkcja często nie jest pomocna.
  6. Ale „ścisłe subs” został włączony, ponieważ renameumożliwia wszystkim ograniczeń ( vars, refs, i subs). Dlatego zamiast tego pojawia się błąd.
  7. renamezakończ z powodu tego błędu. Ponieważ tego rodzaju błąd pojawił się wcześnie, nie podjęto żadnych prób zmiany nazwy pliku, nawet jeśli nie przeszedłeś -n. Jest to dobra rzecz, która często chroni użytkowników przed niezamierzonymi zmianami nazw plików, a czasami nawet przed faktyczną utratą danych.

Dziękuję Zannie , która pomogła mi rozwiązać kilka ważnych niedociągnięć w bardziej wstępnym szkicu tej odpowiedzi . Bez niej ta odpowiedź byłaby o wiele mniej sensowna i w ogóle nie zostałaby opublikowana.

Eliah Kagan
źródło