Postępowanie z nazwami plików ze specjalnymi pierwszymi znakami (np. ♫)

30

Ostatnio natknąłem się na plik, którego nazwa zaczyna się od znaku „.”. Chciałem skopiować ten plik, załadować go ffmpegi odwołać się do niego na różne inne sposoby w terminalu. Zwykle automatycznie uzupełniam dziwne nazwy plików, ale to się nie udaje, ponieważ nie mogę nawet wpisać pierwszej litery.

Nie chcę przełączać się na mysz, aby wykonać manewr kopiuj-wklej. Nie chcę zapamiętywać wielu kodów dla możliwych scenariuszy. Moje rozwiązanie ad hoc polegało na zmianie vim, wklejeniu !lsi skopiowaniu danego znaku, a następnie wyjściu i wklejeniu go do terminala. To działało, ale jest dość przerażające.

Czy istnieje łatwiejszy sposób radzenia sobie z takimi scenariuszami?

UWAGA: Używam skorupy ryb, jeśli to zmienia rzeczy.

ZirconCode
źródło
7
Czy możesz użyć innych części pliku do utworzenia wyrażenia regularnego do pracy z nim? *restoffile.avilub coś w tym stylu?
slm
1
W tym przypadku pozostała nazwa była mieszanką Kanji i Katakana (japoński skrypt), więc nie jest to łatwe.
ZirconCode
3
Zrozumiałem, pomyślałem, że zapytam. Czy odpowiedź jimmij go rozwiązuje? Czy miałbyś też ochotę wkleić zrzut ekranu z obrażających plików? Byłoby to prawdopodobnie pomocne dla innych, którzy mogą przeczytać to później.
slm
1
Próbuję go teraz uruchomić. Nie wiem, jak opublikować screeny, ale uruchomienie następujących poleceń da ci mój próbny problem:touch '♫ 漢字カ' touch '♫ 漢字タ'
ZirconCode
1
Zsh możesz użyć opcji, aby zakładka dała ci menu, z którego możesz wybrać odpowiedni plik.
Kevin

Odpowiedzi:

35

Jeśli pierwszy znak nazwy pliku jest drukowalny, ale nie ma znaków alfanumerycznych ani białych znaków, możesz użyć [[:punct:]]operatora glob:

$ ls *.txt
f1.txt  f2.txt  ♫abc.txt
$ ls [[:punct:]]*.txt
♫abc.txt
jimmij
źródło
Hmm, nie wiedziałem o tych globalnych operatorach, przeczytałem o nich i nauczyłem się trochę (dziękuję), rozwiązuje to mój problem, który jest pojedynczym dziwnym plikiem w moim reż. Teraz mam ten problem z dużą ilością pliki, czy powinienem zadać nowe pytanie lub zaktualizować to?
ZirconCode,
Zaakceptowałem twoją odpowiedź, jutro opublikuję drugi scenariusz, kiedy będę miał czas. Dziękuję za pomoc.
ZirconCode
6

Najprostsze, jakie ls [^a-zA-Z0-9]*przychodzi mi do głowy, jest dla mnie skuteczne, ale odpowiedź Terdona lepiej jest zwrócić uwagę na opcję powłoki extglob lub nawet podejście niezależne od powłoki.

użytkownik86880
źródło
To dość przyzwoite dźgnięcie w to. Możesz zrobić ls [^[:alnum:]]*to samo. Ale lepiej jest użyć klasy postaci, jaką jest , niż klasy, której nie ma ; stąd ls [[:punct:]]*wyświetli ten plik.
Bogaty
6

ls ma kilka przełączników (takich jak - quote-name, --escape, --literal) do obsługi znaków niedrukowalnych, ale w tym przypadku wydaje się, że znak jest „drukowalny”, ale nie „wpisywalny” (przynajmniej na mojej klawiaturze! ), więc żaden z tych przełączników nie wydaje się pomóc.

Dlatego jako ogólne podejście „brutalnej siły”, aby pozbyć się plików z dowolnymi postaciami w ich nazwach, możesz to zrobić:

$ /bin/ls -1A|cat -n  # list all files (except . and ..), 1 per line, add line numbers
     1  ♫
     2  f1.txt
     3  f2.txt

Znajdź wiersz zawierający niepoprawny plik. Całkiem prawdopodobne, że będzie to pierwsza linia, ale powiedzmy, że to piąta. Wydrukuj wiersz 5 i zakoduj go szesnastkowo:

$ /bin/ls -1A|sed -n 5p|xxd -g 1
0000000: e2 99 ab 0a                                      ....

Ignorując znak 0a (nowy wiersz), skonstruuj łańcuch zmiany znaczenia i użyj opcji -e echa, aby przetłumaczyć znaki zmiany znaczenia:

$ echo -e '\xe2\x99\xab'
♫

Teraz możesz skopiować / przenieść / usunąć w następujący sposób:

$ cp -vi $(echo -e '\xe2\x99\xab') better_name
‘♫’ -> ‘better_name’

Ponadto, jeśli nie jesteś ograniczony do używania skryptu powłoki, możesz to zrobić w Pythonie w następujący sposób:

$ python
>>> import os
>>> os.listdir('.')
[ ..., '\xe2\x99\xab', ... ]
>>> print '\xe2\x99\xab'
♫
>>> import shutil
>>> shutil.copy('\xe2\x99\xab', 'better_name')

Korzystając z tego podejścia, możesz przetwarzać wiele plików, wystarczy napisać logikę wyboru właściwych plików i zmiany ich nazwy bez blokowania itp.

for f in os.listdir('.'):
  if not f.isalnum():
    newname = generate_newname(f)
    if not os.path.exists(newname):
      shutil.copy(f, newname)
    else:
      print newname, 'already exists!'
Matthew Breithaupt
źródło
5

Podobnym podejściem byłoby wylistowanie wszystkich plików, które nie zaczynają się od „normalnych” znaków. W bash możesz to zrobić za pomocą

$ shopt -s extglob
$ ls !([[:alpha:]]*)

Nie wydaje się to jednak dostępne fish, więc możesz użyćfind zamiast tego :

$ find . -type f -not -name '[[:alpha:]]*'
terdon
źródło
4

Zmień nazwy dowiązań symbolicznych

Jednym z podejść do obsługi nazw plików ze znakami specjalnymi - jako pierwszych znaków lub w innym miejscu w nazwie pliku jest zmiana nazwy na prostszą .

Można tego użyć, nawet jeśli zachowane są oryginalne nazwy plików : Zmień nazwę kopii nazw plików.
Można to zrobić, kopiując pliki, ale także tworząc dowiązania symboliczne lub dowiązania twarde do plików i zmieniając ich nazwy. cptworzy dowiązania symboliczne zamiast kopii z opcją -s(-l dla twardych).

Użyj „detox”, aby wyczyścić nazwy

Do zmiany nazwy w celu wyczyszczenia nazw plików detoxmożna użyć; Zmienia nazwy plików, aby wyczyścić nazwy plików zgodnie z różnymi regułami zdefiniowanymi w detoxrcpliku. Domyślnie znaki UTF8 są właśnie usuwane; Z opcją -s utf_8-onlysą one zastępowane przez _:

$ touch '♫ 漢字カ' ♫foo
$ ls -1
♫foo
♫ 漢字カ
$ detox -s utf_8-only * 
$ ls -1                
_ ___
_foo


„detox” na dowiązaniach symbolicznych

W połączeniu z pracą nad dowiązaniami symbolicznymi, jak opisano powyżej:

$ mkdir orig
$ cd orig 
$ touch '♫ 漢字カ' ♫foo
$ cd ..
$ mkdir clean
$ cd clean 
$ cp -s ../orig/* .
$ ll               
lrwxrwxrwx 1 14 Oct  8 05:52 ♫foo -> ../orig/♫foo
lrwxrwxrwx 1 21 Oct  8 05:52 ♫\ 漢字カ -> ../orig/♫\ 漢字カ
$ ls -1
♫foo
♫ 漢字カ
$ detox --special -s utf_8-only *
$ ll                                
lrwxrwxrwx 1 21 Oct  8 05:52 _\ ___ -> ../orig/♫\ 漢字カ
lrwxrwxrwx 1 14 Oct  8 05:52 _foo -> ../orig/♫foo
Volker Siegel
źródło
2

Nie używam fish, ale dokumentacja mówi, że można wprowadzić znak Unicode, poprzedzając jego kod szesnastkowy znakiem \u(dla znaków 16-bitowych) lub \U(dla znaków 32-bitowych). Myślę, że kod dla jest 491eb, więc możesz zrobić:

mv \U000491ebabc.mp3 abc.mp3

zmienić nazwę ♫abc.mp3.

Zauważ, że potrzebujesz zer wiodących, w przeciwnym razie abcna końcu będą traktowane jako cyfry szesnastkowe i część kodu znaku; dla znaku 32-bitowego musisz wprowadzić 8 cyfr.

Barmar
źródło
2

Nie wiem, czy było tak już w 2014 r., Kiedy zadałeś pytanie, ale w bieżących wersjach fish(od 2019 r.) Możesz nacisnąć Tabdwa razy, aby uzyskać wybór w stylu zsh, w którym możesz użyć klawiszy strzałek, aby wizualnie wybierz żądany plik bez konieczności wpisywania jakiejkolwiek części nazwy pliku.

Stéphane Chazelas
źródło
2

Ryby nie obsługują symboli wieloznacznych ¹ z założenia.

function find_special_filename
    find ! -path './.*' -name '[^-.a-zA-Z0-9_]*' $argv
end

Komenda nie szukać w ukrytych katalogów i wyświetla nazwy plików, które nie zaczynają się od znaków letters, digits, . _ -(cf dokumentacjifind ).

Uwaga: $argv jest specjalną zmienną tablicową (skorupa ryby), która zawiera argumenty funkcji, dlatego podstawowe polecenie może otrzymać dowolne wyrażenie (np. Alias ).

find_special_filename -exec mv '{}' misc/ \;

¹ W rzeczywistości Fish obsługuje rozwijanie nawiasów (rozszerzenie zmiennej tablicowej), ale Bash używa innej terminologii (rozszerzenia parametrów i nazw plików).

Fólkvangr
źródło
1

Użyj zshi wpisz, co będzie dalej. ZSH obsługuje automatyczne uzupełnianie rozmyte i może sobie z tym poradzić. (Jest to szczególnie miłe z wtyczką OH-MY-ZSH .)

Martin Thoma
źródło
0

Nie powiedziałeś, czy chcesz zachować te problematyczne nazwy plików. Jednym z rozwiązań może być „rozwiązanie” problemu raz na zawsze poprzez zmianę nazwy (części lub wszystkich) plików na nazwy, które można wpisać, uruchamiając ten skrypt:

#!/bin/sh
for old in *
do
      printf "%s ...? " "$old"
      if read new  &&  [ "$new" != "" ]
      then
             mv -i "$old" "$new"
      fi
done

Spowoduje to wyświetlenie istniejących nazw plików, a po nich każdego ...?. Wystarczy wpisać, Enteraby pozostawić plik bez zmian; lub wpisz nową nazwę, aby zmienić nazwę. Ta -iopcja spowoduje wyświetlenie monitu o potwierdzenie zastąpienia, jeśli podasz nazwę innego istniejącego pliku.

Ten skrypt można zmodyfikować na kilka sposobów:

  • Możesz zmodyfikować symbol wieloznaczny ( *) na coś bardziej restrykcyjnego, np. *.avi *.movAbyś nie musiał patrzeć na każdy plik.
  • Możesz zmienić mvnacp , tak aby zachować kopię pliku z obecną nazwą i utworzyć (tymczasowe?) Kopiowanie z typeable nazwy.
  • Możesz utworzyć nową nazwę pliku na podstawie istniejącej nazwy pliku. Na przykład,

    if read pfx  &&  [ "$pfx" != "" ]
    then
            mv -i "$old" "$pfx$old"
    fi
    

    co pozwala spoliczkować prefiks przed starą nazwą. Jeśli wybierzesz unikalny prefiks, pozwoli to na użycie autouzupełniania.

G-Man mówi „Przywróć Monikę”
źródło