Mam dwa pliki w folderze na moim Ubuntu 16.04:
a1.dat
b1.DAT
Chcę zmienić nazwę, b1.DAT
aby b1.dat
w 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 ...
Odpowiedzi:
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:Usuń
-n
po przetestowaniu, aby faktycznie zmienić nazwę plików.Aby dołączyć ukryte pliki, zmień ustawienia globalne przed uruchomieniem polecenia:
Jeśli chcesz rekursywnie zmieniać nazwy plików, możesz użyć
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: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 -u
np.shopt -u globstar
Są 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
:albo lepiej
Używanie
find ... -exec
z+
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 maszargument list too long
problem, 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ż
rename
każ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 ... -exec
z+
dzieli listę argumentów .źródło
-n
nazwę po zmianie po przetestowaniu - to tylko, aby pokazać, co zostanie zmienioneMożesz to zrobić:
Upuść,
-n
aby nastąpiła faktyczna zmiana nazwy.wzorzec glob
*.DAT
pasuje do wszystkich plików, które kończą się w.DAT
bieżącym kataloguw
rename
podstawieniu,DAT$
meczeDAT
na końcu\L$&
sprawia, że całe dopasowanie jest małe;$&
odnosi się do całego meczuJeśli chcesz zrobić tylko dla
b1.DAT
:Przykład:
źródło
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óbrename
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 ) doprename
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 jakrename
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żeszrename
go uruchomić. To dlatego, że pierwszy argument wiersza polecenia można przejść dorename
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ówrename
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ąpieniefoo
w ciągu posiadanych przez$str
zmienną dobar
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 znaczys/foo/bar/
krótko$_ =~ s/foo/bar/
.Często zdarza się, że
s///
wyrażenie jestrename
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
Therename
komenda nie widziałem*.DAT
!Polecenie jak
rename s/foo/bar/ *.txt
zwykle nie przechodzą*.txt
jako argument wiersza polecenia dorename
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 uruchomieniemrename
narzędzia. Powłoka rozwija globusy do potencjalnie wielu nazw ścieżek i przekazuje je wszystkie, jako osobne argumenty wiersza poleceń, dorename
. 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 dorename
.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łab1.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 pierwszyb1.DAT
nieopcyjny argument jako kod Perla, powstaje pytanie: dlaczego produkuje te błędy „niedozwolone słowo”, gdy uruchamiasz go jako kod Perla?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
lubDAT
, więc gdy interpreter Perla zobaczy kodb1.DAT
, próbuje traktowaćb1
iDAT
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ęcb1.DAT
ocenia łańcuchb1DAT
. To jestb1.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 Perlasay b1.DAT
do interpretera Perla, który go uruchamia, drukującb1DAT
. (W tym poleceniu'
'
cytaty powiedzieć skorupę przekazaćsay b1.DAT
jako pojedynczy argument wiersza polecenia, w przeciwnym razie przestrzeń spowodowałobysay
ib1.DAT
być analizowany jako oddzielne słów iperl
będzie odbierać je jako osobne argumenty.perl
Nie nie patrz cytaty same, jak powłoka je usuwa ).Ale teraz spróbuj pisać wcześniej
use strict;
w skrypcie Perlsay
. Teraz kończy się niepowodzeniem z tego samego rodzaju błędem, który otrzymałeśrename
: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ć tylkosubs
ograniczenie. To polecenie powoduje takie same błędy jak powyżej:Ale zwykle programiści Perla po prostu piszą
use strict;
, co umożliwiasubs
ograniczenie i dwóch innych.use strict;
jest ogólnie zalecaną praktyką. Więcrename
polecenie robi to dla twojego kodu. Dlatego pojawia się ten komunikat o błędzie.4. Podsumowując, tak się stało:
b1.DAT
jako pierwszy argument wiersza poleceń, któryrename
traktował jako kod Perla, aby działał w pętli dla każdego argumentu nazwy ścieżki.b1
iDAT
związane z.
operatorem.b1
iDAT
nie były poprzedzone pieczęciami, dlatego traktowano je jak słowa bez słów.'b1'
i'DAT
„i łączone”. To dalekie od zamierzonego, co pokazuje, że ta funkcja często nie jest pomocna.rename
umożliwia wszystkim ograniczeń (vars
,refs
, isubs
). 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.
źródło