2011-01-17 15 views
5

Znaczy pozbycie specjalnych znaków w nazwach plików, itprekurencyjnie „normalizacji” nazwy plików

zrobiłem skrypt, który może zmieniać nazwy plików rekursywnie [http://pastebin.com/raw.php?i= kXeHbDQw]:

np: przed:

THIS i.s my file (1).txt 

po uruchomieniu skryptu:

This-i-s-my-file-1.txt 

Ok. tutaj jest:

Ale: kiedy chciałem go przetestować „w pełni”, o nazwach takich jak ten:

¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÂÃÄÅÆÇÈÊËÌÎÏÐÑÒÔÕ×ØÙUÛUÝÞßàâãäåæçèêëìîïðñòôõ÷øùûýþÿ.txt 
áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&'()*+,:;<=>[email protected][\]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£.txt 

zawiedzie [http://pastebin.com/raw.php?i=iu8Pwrnr ]:

$ sh renamer.sh directorythathasthefiles 
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>[email protected][]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory 
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>[email protected][]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory 
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>[email protected][]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory 
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>[email protected][]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory 
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>[email protected][]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory 
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>[email protected][]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory 
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>[email protected][]^_`{|}~€‚ƒ„…†....and so on 
$ 

tak "mv" nie może obsługiwać specjalne znaki ..: \

i pracował nad nim przez wiele godzin ..

czy ktoś ma działający? [które mogą obsługiwać znaki [nazwy plików] również w tych 2 liniach?]

+5

[Zaakceptowany wskaźnik odpowiedzi na zero] (http://superuser.com/faq#howtoask) nie tworzy Twojego profilu wyglądać dobrze. – grawity

+4

Proszę nie przekierowywać (http://serverfault.com/questions/223514/recursively-normalize-filenames). –

+2

Dlaczego migracja z superużytkownika? To jest skryptowanie powłoki, a nie programowanie ... – leppie

Odpowiedz

4

Zakładając, że reszta twojego skryptu jest prawidłowa, Twoim problemem jest to, że używasz read, ale powinieneś użyć read -r. Zauważ, jak zniknął odwrócony ukośnik odwrotny:

áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&'()*+,:;<=>[email protected][\]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£.txt 
áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>[email protected][]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£ 
17

mv obsługuje znaki specjalne dobrze. Twój skrypt nie działa.


W przypadkowej kolejności:

  1. Używasz find znaleźć wszystkie katalogi i ls każdy katalog oddzielnie.

    1. Dlaczego używać for DEPTH in... czy można zrobić dokładnie samo z jeden polecenia?

      find -maxdepth 100 -type d 
      
    2. co sprawia, że ​​arbitralne ograniczenie głębokości niepotrzebnego

      find -type d 
      
    3. Nie kiedykolwiek analizować wyjście ls, szczególnie jeśli można pozwolić find rączkę, że zbyt

      find -not -type d 
      
    4. Upewnij się, że działa w najgorszym możliwym przypadku:

      find -not -type d -print0 | while read -r -d '' FILENAME; do 
      

      Zatrzymuje read od jedzenia pewnych ucieczek i dławiąc się nazwach zawierających znaki nowej linii.

  2. Jesteś powtórzenie całego cyklu ls | replace dla każdy pojedynczy znak. Nie - to zabija wydajność. Zapętlaj po każdym katalogu wszystkie pliki raz po i po prostu użyj wielu sed lub wielu zamienników w jednym poleceniu sed.

    sed 's/á/a/g; s/í/i/g; ...' 
    

    (miałem zamiar zaproponować sed 'y/áí/ai/', ale niestety to nie wydają się działać z Unicode może perl -CS -Mutf8 -pe 'y/áí/ai/' będzie.).

  3. Wciąż myślisz ASCII: „inne znaki specjalne - kody ASCII 33 .. ..255 ". Nie rób tego.

    1. Te dni, większość systemów używać Unicode w kodowaniu UTF-8, który ma dużo szerszy zakres „specjalnych” znaków - tak duże, że wymieniając je jeden po drugim staje się bezcelowe. (Jest to nawet wielobajtowy - "e" to jeden bajt, "" "to trzy bajty.)

    2. Prawdziwe ASCII ma 128 znaków. Obecnie masz na myśli zestawy znaków ISO 8859 (czasami nazywane "ANSI") - w szczególności ISO 8859-1. Ale idą aż do 8859-16, a tylko część "ASCII" pozostaje taka sama.

  4. echo -n $(command) jest raczej bezużyteczny.

  5. Istnieje wiele łatwiejszych sposobów na znalezienie katalogu i nazwy systemu z podaną ścieżką. Na przykład, można zrobić

    directory=$(dirname "$path") 
    oldnname=$(basename "$path") 
    # filter $oldname 
    mv "$path" "$directory/$newname" 
    
  6. Czy nie korzystanie egrep do sprawdzania błędów. Sprawdź kod powrotu programu. (Tak jak już robisz z cd.)

  7. Zamiast odfiltrowywać inne błędy, ...

    if [[ -e $directory/$newname ]]; then 
        echo "target already exists, skipping: $oldname -> $newname" 
        continue 
    else 
        mv "$path" "$directory/$newname" 
    fi 
    
  8. tona sed 's/------------/-/g' połączeń można zmienić na jednym regexp:

    sed -r 's/-{2,}/-/g' 
    
  9. W [ ] sw tr [foo] [bar] są niepotrzebne. Po prostu powodują, że tr zastąpi [ na [ i ] na ].

  10. Poważnie?

    echo "$FOLDERNAME" | sed "s/$/\//g" 
    

    Co z tym zamiast tego?

    echo "$FOLDERNAME/" 
    

I wreszcie, należy detox.

+6

+10 tylko za walkę z tym bałaganem. +10 dla detox. Niestety 'tr' również nie obsługuje Unicode. Podczas gdy 'grep' rozumie klasy równoważności (' [[= a =]] 'pasuje 'aàâãäå'), nie wydaje się ani' sed', 'tr' ani' gawk'. –

+0

@Dennis: GNU 'sed' obsługuje' [[= a =]] '. – grawity

6

Spróbuj czegoś takiego:

find . -print0 -type f | awk 'BEGIN {RS="\x00"} { printf "%s\x00", $0; gsub("[^[:alnum:]]", "-"); printf "%s\0", $0 }' | xargs -0 -L 2 mv 

użycie xargs (1) zapewni, że każda nazwa pliku minęło dokładnie jako jeden parametr. awk (1) służy do dodawania nowej nazwy zaraz po starej.

Jeszcze jedna sztuczka: sed -e 's/- +/-/g' zastąpi grupy o więcej niż jednym "-" dokładnie jednym.

+2

Dobra, to seksowne wykorzystanie awk i xargów. – MikeyB

1

Fuj ...

Kilka wskazówek do czyszczenia skrypt:

** Zastosowanie sed zrobić tłumaczenie na wielu znaków na raz, że będzie oczyścić rzeczy i ułatwia zarządzanie:

dev:~$ echo 'áàaieeé!.txt' | sed -e 's/[áàã]/a/g; s/[éè]/e/g' 
aaaieee!.txt 

** zamiast zmiany nazwy pliku dla każdy zmian, należy uruchomić wszystkie filtry następnie zrobić jeden ruch

$ NEWNAME='áàaieeé!.txt' 
$ NEWNAME="$(echo "$NEWNAME" | sed -e 's/[áàã]/a/g; s/[éè]/e/g')" 
$ NEWNAME="$(echo "$NEWNAME" | sed -e 's/aa*/a/g')" 
$ echo $NEWNAME 
aieee!.txt 

** zamiast robić ls | read ... pętli, przeznaczenie:

for OLDNAME in $DIR/*; do 
    blah 
    blah 
    blah 
done 

** oddzielić swoją logikę do ścieżek i zmiany nazwy na dwa skrypty. Jeden skrypt znajduje pliki, które muszą zostać zmienione, jeden skrypt obsługuje normalizację pojedynczego pliku. Gdy nauczysz się polecenia "znajdź", zobaczysz, że możesz rzucić pierwszy skrypt :)

+0

Być może miałeś na myśli "dla OLDNAME w" $ DIR "/ * – marco

+0

@marco: nazwij go pseudokodą :) Właściwie to usunąłem go, próbując sobie przypomnieć, jak obejść błąd markdown w bloku kodu. – MikeyB