Bash regex, aby zmienić nazwę zestawu plików

4

Muszę zmienić nazwę zestawu plików, używając renamepolecenia (z wyrażeniem regularnym). Po kilku próbach nie jestem w stanie znaleźć wyrażenia, które uzyska oczekiwany wynik.

Mam taki wzór pliku:

prefix_ some_name _other.txt

Wszystkie pliki zaczynają się od „ prefix_” i kończą się na „ _other.txt”, a część some_name może składać się z wielu (alfanumerycznych) słów oddzielonych podkreślnikami. Możliwe jest więc:

prefix_one_name_other.txt
prefix_this_is_my_name_1_this1_other.txt

Muszę zmienić nazwy takich plików:

other_one-name_ datetime 
other_this-is-my-name-1-this1_ datetime

Innymi słowy:

  • Musisz usunąć „ prefix” (pozostawiając podkreślenie)
  • otherToken „ ” przechodzi na początek nazwy pliku
  • W some_name przekonwertuj podkreślenie (_) na myślnik (-)
  • Znak podkreślenia na końcu nazwy pliku (po jakiejś nazwie ) musi pozostać
  • Musisz usunąć .txtrozszerzenie, zastąpione datetime .

Co próbowałem:

rename 's/fw_([a-z]+)_(\d)_(\w+\d)_(\w+)\.txt/$4_$1-$2-$3_'$datahora'/' *.txt

$datahorama wartość daty / godziny (przetestowano). Działa to zgodnie z oczekiwaniami

prefix_name_1_gnt1_other.txt

ale nie z

prefix_other_name_2_gnt2_other.txt

Gdzie popełniłem błąd? Jak inaczej mogę to osiągnąć?

Zawiesiłem zdanie, ponieważ na razie nie jestem w stanie znaleźć wyrażenia regularnego, które działałoby dla wszystkich posiadanych nazw plików. Wiem, że pierwszy element w ciągu jest zawsze prefixczęścią, a ostatni element jest wtedy other.txtczęścią ciągu. Możliwe jest podzielenie łańcucha na tablicę i uzyskanie elementów potrzebnych do zbudowania nowej nazwy. W rzeczywistości coś takiego.

datahora="20140718-080000"
arrfiles=( *.txt )
for curfile in ${arrfiles[*]}
do
    arrparts=( ${curfile//_/ } )
    numitems=${#arrparts[*]}
    newname=""
    for (( c=1; c<numitems-1; c++ ))
    do
        newname+="${arrparts[c]}-"
    done
    newname=${newname%-}
    arrparts[numitems-1]=${arrparts[numitems-1]/.txt/}
    newname="${arrparts[numitems-1]}_${newname}_$datahora"
    echo "$curfile pasa a $newname"
    mv ${curfile} ${newname}
done

Po zrobieniu tego w ten sposób, spróbowałem ponownie @peterph sugestię, a na koniec skończyłem z kilkoma kombinacjami regexów. Pomyśl coś takiego:

rename 's/_/-/g' *.txt
rename 's/^fw-(.*)-([^-]*)(\.txt)/$2.$1$3/' *.txt
rename 's/(\w+)\.(.*)(\.txt)/$1_$2_'$datahora'/' *.txt

Nie jestem pewien, jakie jest najlepsze podejście. Moim zdaniem wariant wyrażenia regularnego wydaje się bardziej elegancki, ale potrzebuję trzech operacji zmiany nazwy (dostępu trzy razy na dysk), aby wykonać pracę, podczas gdy arraywariant zapisuje tylko raz na dysku.

¿Co sądzisz o tych dwóch rozwiązaniach? ...

Dzięki jeszcze raz.

Ferran
źródło
Może lepiej pasować do systemów Unix i Linux SE .
peterph
Czy mogę przenieść to pytanie?
Ferran

Odpowiedzi:

2

O ile nie renamemożesz zaakceptować wielu poleceń podstawiania, a katalog główny nazwy pliku ( some_name) może zawierać więcej niż jeden znak podkreślenia, musisz to zrobić w dwóch krokach: a) zastąpienie znaków podkreślenia myślnikami ib) (ponowne) przenoszenie fragmentów w pliku nazwy.

Wyrażenia regularne, których szukasz, mogą na przykład:

rename 's/_/-/g' *.txt
rename 's/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt

Pierwszy wykonuje podkreślenie, aby przerywać tłumaczenia, a drugi zamienia katalog główny i sufiks oraz dołącza zawartość DATETIMEzmiennych środowiskowych do nazw. I oczywiście pomija prefiks i rozszerzenie.

[^-]*Część pasuje dowolny ciąg nie zawierający kreskę. Jeśli sufiks jest zawsze taki sam, możesz umieścić go tam dosłownie, jak ma to miejsce w przypadku prefiksu (i odwrotnie - jeśli prefiks może się różnić, użyj, ^[^-]*-aby dopasować go jako dowolny ciąg niezawierający myślnika znajdujący się między początkiem pliku imię i (a więc) pierwszy myślnik).

Jeśli renameobsługujesz wiele poleceń, po prostu połącz je:

rename 's/_/-/g;s/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt
Peter
źródło
Dziękuję bardzo Peter, ale wydaje się, że to nie działa tak, jak się spodziewaliśmy. Dostaję tę konwersję: other-this-is-1-name-20140717-093458 Kiedy szukam: other_this-is-1-name_20140717-093458 zwróć uwagę na podkreślenie po otheri przed datetime.
Ferran
Z niewielkimi zmianami w wyrażeniu regularnym @peterph sugeruj i połącz z innymi wyrażeniami regularnymi, aby wykonać pracę. Zobacz moją nową edycję, aby uzyskać więcej informacji.
Ferran
Przepraszam, nie przeczytałem pytania wystarczająco uważnie - zaktualizowałem odpowiedź. Musisz tylko zmienić separatory wokół katalogu głównego nazwy pliku: po prostu zastąp myślniki wokół $2podkreślnikami w drugim wyrażeniu regularnym.
peterph