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 rename
polecenia.
Pierwsza część tej odpowiedzi dotyczy relacji między rename
Perlem a tym, w jaki sposób rename
uż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 rename
pojawi się dziwny komunikat o błędzie, dodaj „Perl” do wyszukiwania.
W Debianie i Ubuntu rename
polecenie 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 prename
polecenia. W nowszych wersjach wskazuje zamiast tego na nowszą file-rename
komendę. 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 rename
w pozostałej części tej odpowiedzi.
Kiedy używasz rename
polecenia, nie tylko uruchamiasz kod Perla, który napisał ktoś inny. Piszesz także własny kod Perla i każesz rename
go uruchomić. To dlatego, że pierwszy argument wiersza polecenia można przejść do rename
polecenia, inne niż argumentów opcjonalnych , takich jak -n
, składa się z rzeczywistego kodu Perl . rename
Komenda 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 rename
nazw ś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 foo
w ciągu posiadanych przez $str
zmienną do bar
lub 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 rename
argumentem 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 rename
jest 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 *.dat
The rename
komenda nie widziałem *.DAT
!
Polecenie jak rename s/foo/bar/ *.txt
zwykle nie przechodzą *.txt
jako argument wiersza polecenia do rename
programu, a nie chcesz go , chyba że masz plik, którego nazwa jest dosłownie *.txt
, co mam nadzieję, że nie.
rename
nie 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 rename
narzę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 *.dat
twoja powłoka rozwinęła *.DAT
się 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 *.DAT
i wszelkie rozwinięte z - *.dat
pojawił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ż rename
traktuje swój pierwszy b1.DAT
nieopcyjny 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 $foo
jest 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 b1
lub DAT
, więc gdy interpreter Perla zobaczy kod b1.DAT
, próbuje traktować b1
i DAT
jak 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.DAT
ocenia łańcuchb1DAT
. To jest b1.DAT
zł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.DAT
do interpretera Perla, który go uruchamia, drukując b1DAT
. (W tym poleceniu '
'
cytaty powiedzieć skorupę przekazać say b1.DAT
jako pojedynczy argument wiersza polecenia, w przeciwnym razie przestrzeń spowodowałoby say
i b1.DAT
być analizowany jako oddzielne słów i perl
będzie odbierać je jako osobne argumenty. perl
Nie 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 subs
ograniczenie. 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 subs
ograniczenie i dwóch innych. use strict;
jest ogólnie zalecaną praktyką. Więc rename
polecenie robi to dla twojego kodu. Dlatego pojawia się ten komunikat o błędzie.
4. Podsumowując, tak się stało:
- Powłoka została przekazana
b1.DAT
jako pierwszy argument wiersza poleceń, który rename
traktował jako kod Perla, aby działał w pętli dla każdego argumentu nazwy ścieżki.
- To miało oznaczać
b1
i DAT
związane z .
operatorem.
b1
i DAT
nie były poprzedzone pieczęciami, dlatego traktowano je jak słowa bez słów.
- 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.
- 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.
- Ale „ścisłe subs” został włączony, ponieważ
rename
umożliwia wszystkim ograniczeń ( vars
, refs
, i subs
). Dlatego zamiast tego pojawia się błąd.
rename
zakoń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.