Dlaczego moje nazwy folderów tak się skończyły i jak mogę to naprawić za pomocą skryptu?

15

Przepraszam, jeśli ma to gdzie indziej odpowiedź, nie mam pojęcia, jak wyszukać mój problem.

Uruchomiłem kilka symulacji na serwerze redhat Linux HPC, a mój kod do obsługi struktury folderów w celu zapisania danych wyjściowych miał niefortunny błąd. Mój kod Matlab do utworzenia folderu to:

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

gdzie sp.run_numberbyła liczba całkowita. Zapomniałem przekonwertować go na ciąg, ale z jakiegoś powodu uruchomienie mkdir(folder);(w Matlabie) nadal się powiodło. W rzeczywistości symulacje przebiegły bez żadnych problemów, a dane zostały zapisane w odpowiednim katalogu.

Teraz, gdy struktura folderów jest sprawdzana / drukowana, otrzymuję następujące sytuacje:

  • Kiedy próbuję uzupełnić kartę autouzupełnianiem: run_ run_^A/ run_^B/ run_^C/ run_^D/ run_^E/ run_^F/ run_^G/ run_^H/ run_^I/
  • Kiedy używać ls: run_ run_? run_? run_? run_? run_? run_? run_? run_? run_? run_?.
  • Kiedy przesyłam do mojego komputera Mac przy użyciu rsync, --progressopcja pokazuje: run_\#003/itd. Z (zakładam) liczbą pasującą do liczby całkowitej w sp.run_numberdopełnieniu do trzech cyfr, więc 10. przebieg jestrun_\#010/
  • Kiedy przeglądam foldery w wyszukiwarce, widzę run_ run_ run_ run_ run_ run_ run_ run_ run_ run_?
  • Patrząc na to pytanie i używając polecenia ls | LC_ALL=C sed -n lotrzymuję:
run_$
run_\001$
run_\002$
run_\003$
run_\004$
run_\005$
run_\006$
run_\a$
run_\b$
run_\t$
run_$

Nie mogę zarządzać cdfolderami przy użyciu tych reprezentacji.

Mam tysiące tych folderów, więc muszę to naprawić za pomocą skryptu. Która z tych opcji jest poprawną reprezentacją folderu? Jak mogę programowo odwoływać się do tych folderów, aby zmienić ich nazwę na poprawnie sformatowaną nazwę za pomocą skryptu bash? I chyba ze względu na ciekawość, jak do diabła to się stało?

Phill
źródło
4
„Gdy próbuję utworzyć zakładkę autouzupełniania: ... Jeśli spróbuję wpisać ...” Dlaczego wpisz i nie pozwól, by autouzupełnianie zakończyło się, jeśli dla Ciebie? Nie ^Ajest też dosłownie ^po nim A, ale Ctrl-A (możesz wpisać go za pomocą Ctrl-V Ctrl-A, ponieważ Ctrl-A jest ogólnie skrótem do powłoki).
muru
@muru, który nie działa ... Dotarłem tak daleko run_i muszę coś
napisać
Przepraszam skomentowałem, zanim zobaczyłem twoją edycję, która udaje mi się wprowadzić mnie przez cd
Phill
Możliwy duplikat nazwy pliku Unicode Select w Bash
muru
9
BTW, „jakimś powodem”, dlaczego mkdir w matlab to zrobił, ponieważ TYLKO niepoprawne znaki w nazwie pliku lub katalogu w systemach plików unix to NUL i ukośnik /. Każdy inny znak jest prawidłowy, w tym znaki kontrolne. Nie wiem, co zrobiłby Matlab, gdyby sp.run_number miał wartość 0 (prawdopodobnie przerwałby z błędem lub produkował run_, ponieważ bajt NUL zakończyłby ciąg nazwy katalogu). Oczywiście byłoby to również problematyczne dla 16-bitowych (lub wyższych) wartości, które zawierały bajt NUL, a także zmieniałoby się w zależności od endianowości systemu z uruchomionym matlabem.
cas

Odpowiedzi:

26

Możesz użyć perla rename narzędzia (aka prenamelub file-rename), aby zmienić nazwę katalogów.

UWAGA: Nie należy tego mylićrename od util-linux, lub jakakolwiek inna wersja.

rename -n 's/([[:cntrl:]])/ord($1)/eg' run_*/

To używa Perla ord() funkcji do zastąpienia każdego znaku kontrolnego w nazwie pliku numerem porządkowym tego znaku. np. ^Astaje się 1, ^Bstaje się 2 itd.

-nOpcja jest na sucho, aby pokazać, corename byłoby zrobić jeśli go. Usuń go (lub zamień -vna pełne dane wyjściowe), aby faktycznie zmienić nazwę.

The eModyfikator w s/LHS/RHS/egprzyczyn operacyjnych perl wykonać RHS (zastąpienie) jako kod Perl, i $1jest dopasowane dane (znak kontrolny) od LHS.

Jeśli chcesz, by w nazwach plików występowały liczby uzupełnione zerami, możesz połączyć je ord()zsprintf() . na przykład

$ rename -n 's/([[:cntrl:]])/sprintf("%02i",ord($1))/eg' run_*/ | sed -n l
rename(run_\001, run_01)$
rename(run_\002, run_02)$
rename(run_\003, run_03)$
rename(run_\004, run_04)$
rename(run_\005, run_05)$
rename(run_\006, run_06)$
rename(run_\a, run_07)$
rename(run_\b, run_08)$
rename(run_\t, run_09)$

Powyższe przykłady działają wtedy i tylko wtedy, gdy sp.run_number w skrypcie Matlab był w zakresie 0..26 (więc tworzył znaki kontrolne w nazwach katalogów).

Aby poradzić sobie z KAŻDYM znakiem 1-bajtowym (tj. Od 0..255), użyjesz:

rename -n 's/run_(.)/sprintf("run_%03i",ord($1))/e' run_*/

Jeśli sp.run_numbermoże być> 255, musisz użyć Perlaunpack() funkcji zamiast ord(). Nie wiem dokładnie, jak Matlab wyprowadza nieprzekształcone int w ciągu, więc będziesz musiał eksperymentować. Zobacz perldoc -f unpackszczegóły.

np. następujące polecenie rozpakuje zarówno 8-bitowe, jak i 16-bitowe wartości bez znaku i wstawi je zerowo do szerokości 5 cyfr:

 rename -n 's/run_(.*)/sprintf("run_%05i",unpack("SC",$1))/e' run_*/
cas
źródło
Dzięki za szczegóły! Próbuję to przetestować z -nopcją, ale to mówi mi, że jest to nieprawidłowa opcja - informacje o wersji dają mi, rename from util-linux 2.23.2więc nie jestem pewien, czy to ta sama funkcja
Phill
3
dlatego podałem wersję perlarename narzędzia. util-linuxjest renameto bardzo różne, mniej zdolny, a możliwych opcji są niekompatybilne. jeśli korzystasz z Debiana lub podobnego, spróbuj zainstalować file-renamepakiet. w przeciwnym razie zainstaluj odpowiedni pakiet dla swojej dystrybucji. może być już zainstalowany, spróbuj uruchomić prenamelub file-renamezamiast po prostu rename.
cas
Tak, myślałem, że tak jest. Zobaczę, czy uda mi się sprawić, by jeden z nich zadziałał. Jeszcze raz dziękuję za poświęcenie czasu na pomoc!
Phill
11

I chyba ze względu na ciekawość, jak do cholery to się stało?

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

gdzie sp.run_numberbyła liczba całkowita. Zapomniałem przekonwertować go na ciąg, ale z jakiegoś powodu działa mkdir(folder); (w Matlabie) nadal się powiodło.

Wygląda więc na to, że mkdir([...])w Matlab łączy elementy tablicy, aby zbudować nazwę pliku jako ciąg. Ale zamiast tego podałeś jej liczbę, a liczby są takimi, jakimi naprawdę są znaki na komputerze. Kiedy sp.run_numberbył 1, dał ci znak z wartością 1, a następnie znak z wartością 2itp.

Są to znaki kontrolne, nie mają symboli do wydrukowania, a wydrukowanie ich na terminalu miałoby inne konsekwencje. Zamiast tego często są reprezentowane przez różne rodzaje ucieczek: \001(ósemkowe), \x01(szesnastkowe),^A znaków wszystkie są typowymi reprezentacjami dla postaci o wartości 1. Znak o wartości zero jest nieco inny, to bajt NUL, który służy do oznaczania końca łańcucha w C i w wywołaniach systemowych Unix.

Jeśli przekroczysz 31, zaczniesz widzieć znaki do wydrukowania, 32 to spacja (choć mało widoczna), 33 = !, 34 =" itd.

Więc,

  • run_ run_^A/ run_^B/- Pierwszy run_odpowiada bajtowi zero, łańcuch kończy się tam. Inne pokazują, że twoja powłoka lubi używać wyświetlania kodów kontrolnych ^A. Notacja wskazuje również na to, że znak o wartości liczbowej 1 można wprowadzić jako Ctrl-A, chociaż powinieneś powiedzieć powłoce, aby interpretowała nie jako znak kontrolny, ale jako literał, Ctrl-V Ctrl-Apowinna to zrobić przynajmniej w Bash.

  • ls: run_ run_? run_?- lsnie lubi drukować znaków niedrukowalnych na terminalu, zastępuje je znakami zapytania.

  • rsync: run_\#003/- ten jest dla mnie nowy, ale idea jest taka sama, odwrotny ukośnik oznacza ucieczkę, a reszta to wartość liczbowa znaku. Wydaje mi się, że liczba tutaj jest ósemkowa, jak w bardziej powszechnej \003.

  • za pomocą polecenia ls | LC_ALL=C sed -n l... run_\006$ run_\a$ run_\b$ run_\t$- \a, \bi \tatom C uchodzi alarmu (dzwon) backspace i zakładki, odpowiednio. Mają wartości liczbowe 7, 8 i 9, więc powinno być jasne, dlaczego po nich \006. Użycie tych znaków C jest kolejnym sposobem na oznaczenie znaków kontrolnych. Końcowe znaki dolara oznaczają koniec linii.

Co do tego cd, zakładając, że moje założenia są słuszne, cd run_powinien przejść do tego jednego katalogu bez nieparzystych znaków końcowych i cd run_?powinien dać błąd, ponieważ znak zapytania jest znakiem globalnym, który pasuje do dowolnego pojedynczego znaku, i istnieje wiele pasujących nazw plików, ale cdtylko oczekuje jednego.

Która z tych opcji jest poprawną reprezentacją folderu?

Wszystkie w pewnym sensie ...

W bashu można użyć \000i \x00ucieczek wewnątrz $'...'cudzysłowów do reprezentowania znaków specjalnych, więc $'run_\033(ósemkowe) lub$'run_\x1b' odpowiadać katalogu o wartości 27 znaków (co zdarza się ESC). (Nie sądzę, że Bash obsługuje znaki ucieczki z liczbami dziesiętnymi).

Odpowiedź cas ma skrypt do zmiany nazwy, więc nie pójdę tam.

ilkkachu
źródło
Jeśli jest to GNU ls, istnieje kilka opcji cytowania, w tym -b/ --escapei --quoting-style=, lub QUOTING_STYLEzmienna środowiskowa, które kontrolują sposób wyświetlania znaków niedrukowalnych. Nie sądzę jednak, aby istniała opcja, aby wolała ósemkowe ucieczki od wersji postaci.
Toby Speight
3

Najłatwiej byłoby utworzyć niewłaściwą nazwę pliku i poprawną nazwę pliku w tym samym środowisku, w którym doszło do nieszczęścia, a następnie po prostu przenieść / zmienić nazwę folderów na prawidłowe nazwy.

Aby uniknąć kolizji między istniejącymi nazwami, lepiej użyj innego folderu docelowego.

./saveLocationA/wrongname1 -> ./saveLocationB/correctname1
./saveLocationA/wrongname2 -> ./saveLocationB/correctname2
./saveLocationA/wrongname3 -> ./saveLocationB/correctname3

Jeśli to możliwe, wolałbym naprawić skrypt i po prostu uruchomić go ponownie; naprawienie dziwnych sekcji zwłok prawdopodobnie kosztuje więcej i może wprowadzić nowe problemy.

Powodzenia!

Piotr
źródło