Jak mogę usunąć plik, którego nazwa pliku zawiera znaki niedrukowalne?


46

Jakoś udało mi się stworzyć plik, który wydaje się nie mieć nazwy pliku. W poniższym wątku znalazłem informacje na temat uzyskiwania dodatkowych informacji o pliku .

Jednak wypróbowałem niektóre z wymienionych sugestii i nie mogę usunąć pliku. Nie jestem pewien, co zrobiłem, aby go utworzyć, ale stało się to podczas próby skopiowania pliku xml.

Niektóre informacje o pliku są następujące;

> ls -lb
total 296
-rw-r--r--   1 voyager  endeavor  137627 Jan 12 12:49 \177

> file *
:               XML document

> ls -i
 417777   

Próbowałem znaleźć za pomocą przełącznika inum, a następnie przesłać go do rm, ponieważ wydawało się to najbardziej niezawodnym sposobem na pozbycie się go. Jednak przykład podany na dole wątku, do którego link znajduje się poniżej, zawiódł mnie. Przykładem było:

> find -inum 41777 -exec ls -al {} \;
find: illegal option -- i
find: [-H | -L] path-list predicate-list

więc najpierw spróbowałem użyć listy ścieżek, jak poniżej, ale to też nie działało:

> find . -inum 41777 -exec ls -al {} \;

Nie jestem pewien, co to jest znak niedrukowalny \ 177 ani w jaki sposób mogę przekazać to rmpolecenie, ale naprawdę chcę się upewnić, że nie zepsuję żadnych innych plików / katalogów podczas próby usunięcia tego pliku.


1
\177jest DELznakiem ASCII .
Keith Thompson

9
417777! = 41777
CVn

Doskonały punkt Michael. Prawdopodobnie dlatego moje polecenie nigdy nie zadziałało.
Pan Moose

@KeithThompson Czy jest ósemkowy bez prowadzenia 0?
kot

1
@cat: W tym kontekście tak. Jest zgodny ze składnią C dla literałów łańcuchowych i znakowych.
Keith Thompson

Odpowiedzi:


46

Plik ma nazwę, ale składa się ze znaków niedrukowalnych. Jeśli używasz ksh93, bash, zsh, mksh lub FreeBSD sh, możesz spróbować go usunąć, podając jego nazwę, która nie może być wydrukowana. Najpierw upewnij się, że nazwa jest poprawna z: ls -ld $'\177' Jeśli pokazuje odpowiedni plik, użyj rm:rm $'\177'

Innym (nieco bardziej ryzykownym) podejściem jest zastosowanie rm -i -- *. Z opcją -i rm wymaga potwierdzenia przed usunięciem pliku, więc możesz pominąć wszystkie pliki, które chcesz zachować oprócz jednego.

Powodzenia!


1
Podejrzewam, rm -i *że nie zadziałałoby w tym przypadku; z powodu nowych linii powłoka rozwijałaby się *do czegoś, rmco nie rozpoznałoby nazwy pliku.
Keith Thompson

6
@KeithThompson: Ekspansja globu w powłoce nie podlega dalszej interpretacji. W rozwiniętej nazwie nie byłoby nowego wiersza, jeśli nie zawierałoby go.
Christoffer Hammarström

@ ChristofferHammarström: Hmm. Zrobię kilka eksperymentów i skomentuję dalej.
Keith Thompson

@ ChristofferHammarström: W rozwiniętej nazwie występuje nowa linia, ponieważ nazwa pliku zawiera znaki nowej linii. Ale wydaje się, że nie doceniłem bashi GNU rm. W moim systemie rm *, rm -i *i rm Ctrl-V DEL TAB ENTERwszystko działa poprawnie, nawet jeśli nazwa pliku zawiera znak nowej linii (kiedyś "\177foo\nbar\n"). Niektóre polecenia nie radzą sobie dobrze z takimi nazwami plików, ale narzędzia GNU wydają się niezawodne; wersje tych samych narzędzi inne niż GNU mogą nie zachowywać się tak samo. W każdym razie przydatne są różne sztuczki zawarte w tych odpowiedziach.
Keith Thompson

1
@KeithThompson, każda powłoka poprawnie obsłuży globowanie plików, nie tylko Bash, i rmnigdy nie wykonuje żadnej formy podziału słów, niezależnie od tego, czy jest to GNU, rmczy nie. Powłoka rozszerza globusy i po wykonaniu żądanego polecenia przekazuje wynikowe nazwy plików jako argumenty rozdzielone zerami (w kodzie C). Niebezpieczne jest przesuwanie listy plików do czegoś, co używa znaku nowej linii jako ogranicznika; zobacz Dlaczego zapętlanie wyjścia find jest złą praktyką?
Wildcard

23

Dla tych, którzy go używają, vimuruchom go w bieżącym katalogu roboczym:

$ vim ./ 

i przejdź do pliku za pomocą klawiszy strzałek lub j/k. Następnie naciśnij Shift+Di potwierdź usunięcie za pomocą y.


generalnie boję się vima, ale to działało naprawdę dobrze
tofutim

9

Prawdopodobnie istnieje sposób na przekazanie nazwy pliku rm, ale jeśli martwisz się o coś zepsuć, możesz użyć menedżera plików GUI. Emacs posiada tryb edycji katalogu, z którego można korzystać, jeśli jest zainstalowany:

  1. Otwórz folder w emacs. Możesz uruchomić emacs /path/to/folderlub otworzyć emacsa, nacisnąć Esc+ xi uruchomić dired, co wyświetli monit o podanie ścieżki

    http://so.mrozekma.com/unix-dired-delete1.png

  2. Przejdź do wiersza z plikiem, który chcesz usunąć, i naciśnij d. Powinieneś zobaczyć Dna lewym marginesie obok pliku:

    http://so.mrozekma.com/unix-dired-delete2.png

  3. Naciśnij, xaby zapisać zmiany. Zostanie wyświetlony monit o upewnienie się, że chcesz usunąć plik; naciśnij y:

    http://so.mrozekma.com/unix-dired-delete3.png


1
Wygląda na świetny pomysł, ale nie mogę znaleźć emacsa na tym komputerze. Ryzykując rozpoczęcie świętej wojny ... czy można tego dokonać vim?
Pan Moose

2
Dla tych, którzy go używają, vimuruchom go w bieżącym katalogu roboczym: vim ./i przejdź do pliku za pomocą klawiszy strzałek. Następnie naciśnij Shift+Di potwierdź usunięcie za pomocą y.

@hesse Wow. Nie jest to coś, co spodziewałbym vimsię wesprzeć
Michael Mrozek

1
@MichaelMrozek zgodził się, że ma wszystkie te funkcje, które czasami odkrywam.


8

Wykazałeś już, file *że powłoka glob ( *) jest w stanie pobrać ten plik, więc teraz po prostu zrób rmna tym globu.

  • Przejdź do katalogu, w którym znajduje się plik.
  • Biegać rm -i *
  • Wprowadź ndla wszystkich plików oprócz tego z pozornie niewidoczną nazwą pliku

7

Plik faktycznie ma nazwę, ale składa się ze znaków niedrukowalnych.

Prostą metodą jest poleganie na zakończeniu powłoki: naciskaj Tabwielokrotnie, aż wstawiona zostanie „dziwna” nazwa pliku. Musisz mieć skonfigurowaną powłokę, aby przełączała się między możliwymi uzupełnieniami:

  • W bash, trzeba się powołać menu-complete, która nie jest związana domyślnie zamiast complete, który jest związany z Tabdomyślnie. Alternatywnie użyj, Esc *aby wstawić uzupełnienia dla wszystkich plików i usunąć te, których nie chcesz usuwać z wiersza poleceń.
  • W Zsh ustawienia domyślne są w porządku; musisz mieć setopt auto_menulub setopt menu_completeustawić.

Jeśli zakończenie nie jest pomocne w twojej powłoce, możesz zadzwonić rm -i -- *i odpowiedzieć „nie”, z wyjątkiem tego konkretnego pliku. Jeśli nazwa pliku nie zaczyna się od znaku drukowalnego, możesz ograniczyć dopasowania do plików, których pierwszy znak nie znajduje się w zestawie znaków drukowalnych:

rm -i [!!-~]*
rm -i [![:print:]]*
rm -i -- [!a-z]

(Uwaga, jeśli wzorzec może pasować do pliku o nazwie -f, który rmtraktuje jako opcję).

Inną metodą jest wywołanie w ls -icelu znalezienia i-węzła pliku (i-węzeł jednoznacznie identyfikuje plik w danym systemie plików), a następnie find -inumdziałanie na tym konkretnym pliku. Ta metoda jest szczególnie przydatna, gdy katalog zawiera wiele plików.

ls -i
# note the inode number 12345
find . -xdev -inum 12345 -delete

Jeśli findnie masz -deleteopcji, zadzwoń rm:

find . -xdev -inum 12345 -exec rm -- {} \;

(Tak, wiem, że większość z nich została już opublikowana. Ale odpowiedzi z odpowiednimi informacjami sprawiają, że jest to bardziej skomplikowane niż powinno.)


4

Spróbuj użyć, echo -eaby zdobyć postać \177, a następnie xargsprzekazać ją rm. echonie przyjmuje przecinków dziesiętnych, więc przekonwertuj na hex: Okazuje się, że 177jest ósemkowa; echowymaga \0(dosłownego, nie zerowego bajtu) przed:

echo -ne '\0177\0' | xargs -0 rm

Czy mówisz, że \ xb1 \ 0 jest szesnastkową reprezentacją \ 177?
Pan Moose

@MrMoose \xb1jest liczbą dziesiętną 177, \0kończy ją zerem i -0mówi xargso przyjęciu nazwy zakończonej znakiem null zamiast nazwy zakończonej znakiem nowej linii.
Kevin

Używam bash na SunOS i kiedy próbowałem twojej komendy, dostałem xargs: niedozwolona opcja - 0. Spojrzałem na stronę podręcznika i nie widziałem opcji zakończenia zerowania. Teraz udało mi się jednak rozwiązać problem. Zobacz odpowiedź oznaczoną jako odpowiedź.
Pan Moose

Twój echo, tak jak jest, wysyła 2 nazwy plików do xargs, a następnie do rm... druga nazwa pliku to \n... Powoduje to błąd lub usunięcie pliku o tej nazwie ('\ n')
Peter. O

@perer Tak, właśnie to zauważyłem. Dodałem, -naby pozbyć się nowej linii.
Kevin

3

Mogę tylko o zagwarantowanie, że plik nie ma nazwy. Zdarza się, że jest to nazwa zawierająca znaki niedrukowalne.

Jeśli rmnie zadziałało, użycie wyniku findz -inumopcją jako argumentu rmraczej nie pomoże; przekaże ten sam argument do rmtego samego problemu.

Plik jest jedynym w tym katalogu, prawda?

Pierwszą rzeczą, którą spróbuję, to cdwejść do katalogu, a następnie wpisać rm ./, a następnie nacisnąć klawisz Tab. Powinno to rozwinąć argument do rzeczywistej nazwy pliku, a powyższe ./powinno uniemożliwić rminterpretację argumentu jako czegoś innego niż nazwa pliku.

Innym rozwiązaniem jest cdprzejście do katalogu nadrzędnego, a następnie użycie rm -r(lub, jeśli to konieczne rm -rf) w katalogu. To powinno usunąć katalog i całą jego zawartość, niezależnie od nazwy. Następnie można użyć mkdirdo odtworzenia katalogu (i być może chmoddo przywrócenia jego uprawnień).

EDYTOWAĆ :

Patrząc ls -lbponownie na dane wyjściowe, myślę, że nazwa pliku zaczyna się od znaku ASCII DEL(który jest zły, a nie śmiertelny) i zawiera jeden lub więcej znaków nowej linii (co jest naprawdę złe). O ile mi wiadomo, rmpolecenie nie może interpretować nowego wiersza jako części nazwy pliku.

Ale unlink()połączenie systemowe może. Oto skrypt Perla, który rzuciłem razem, który będzie iterował pliki w bieżącym katalogu i poprosił o każdy z nich, czy chcesz go usunąć. Nie używa zewnętrznego polecenia, takiego jak rmusuwanie plików, więc powinno być w stanie obsłużyć wszelkie śmieszne znaki obsługiwane przez system plików (cokolwiek innego niż ASCII NULi /).

Wykonanie rm -rlub rm -rfw katalogu zawierającym powinno nadal działać, ale jeśli chcesz usunąć pojedynczy plik, możesz spróbować.

#!/usr/bin/perl

use strict;
use warnings;

opendir my $DIR, '.' or die ".: $!\n";
my @files = sort grep { -f } readdir $DIR;
closedir $DIR;

foreach my $file (@files) {
    my $pfile = printable($file);
    print "Remove $pfile? ";
    my $answer = scalar <STDIN>;
    if ($answer =~ /^[Yy]/) {
        print "Removing $pfile\n";
        unlink $file or warn "Unable to remove $pfile: $!\n";
    }
}

sub printable {
    my($s) = @_;
    $s =~ s/[^ -~]/?/g;
    return $s;
}

(Innym rozwiązaniem, jeśli w katalogu są inne pliki, które chcesz zachować, jest przeniesienie wszystkich innych plików do katalogu tymczasowego, a następnie nuke i odtworzenie katalogu i przeniesienie pozostałych plików z powrotem.)

(Och, i cokolwiek zrobiłeś, aby utworzyć ten plik, spróbuj nie robić tego ponownie!)


1

Jeśli możesz znaleźć dokładnie ten plik za pomocą findpolecenia, możesz użyć -deletepredykatu. Bądź ostrożny i ustaw -deleteostatni parametr polecenia find, bo inaczej będzie bardzo bolało ...


1

Jesteś blisko. Krok po kroku, oto jak usunąć plik według numeru i-węzła.

Najpierw znajdź numer i-węzła żądanego pliku, używając ls -li. Numer i-węzła będzie w pierwszej kolumnie wyniku.

Na przykład:

$ls -li
311010 -rw-rw-r-- 1 me me 3995 Apr  6 16:27 -???\# ;-)

W tym przypadku numer i-węzła to 311010

Służy finddo wyszukiwania pliku według numeru i-węzła.

find . -inum 311010 

Upewnij się, że findzwraca tylko nazwę pliku, który chcesz usunąć. Na przykład:

$find . -inum 311010
./-???\# ;-)

Gdy masz pewność, że findznajdziesz odpowiedni plik, przekaż -deleteparametr do find. Spowoduje to usunięcie pliku za Ciebie.

find . -inum 311010 -delete

0

Inne odpowiedzi są świetne i będą działać w prawie wszystkich środowiskach.

Ale jeśli masz uruchomione GUI, po prostu otwórz katalog w nautilus, delfinach, konquerorze lub ulubionym menedżerze plików i powinieneś być w stanie łatwo zobaczyć i usunąć każdy plik o niedziałającej nazwie za pomocą kilku kliknięć myszką .

Jeśli nie jesteś zwykłym użytkownikiem emacsa lub vima (jak opisano w kilku innych odpowiedziach), może to być najłatwiejszy sposób.


0

Miał podobny problem z plikiem o nazwie „\ 033” (faktyczny znak „Escape”).

for x in $( ls | awk '!/^[A-Z]/ && !/^[a-z]/ && !/^[0-9]/');do rm -i -- $x ; done

Powyższe monituje o usunięcie plików w bieżącym katalogu, które nie zaczynają się od litery lub cyfry.


0

Zamiast tego możesz zmienić nazwę bieżącego katalogu na DirX_OLD, utworzyć nowy katalog z nazwą DirXi przenieść wszystkie wymagane pliki z DirX_OLDdo DirX. Po skopiowaniu plików możesz je usunąćDirX_OLD


0

Na wypadek, gdyby to pomogło komukolwiek dodać moje dwa centy. Udało mi się uzyskać nazwy plików przy użyciu statsymboli wieloznacznych w następujący sposób:

stat -c %N *

Wynik:

`\220g\201\acontexts'
`\220g\201\alogins'
`\220g\201\amodules'
`\220g\201\apolicy'
`\220g\201\asetrans.conf'

Następnie mógłbym użyć ciągów cytowanych w ANSI C (zgodnie z przyjętą odpowiedzią), aby uzyskać dostęp do plików:

rm $'\220g\201\asetrans.conf'

0

Każdy plik ma nazwę, wystarczy znaleźć dokładnie to, które.

Użyję tych plików jako przykładów:

$ touch $'\177' $'\001\002' $'\033\027' a b abc $'a\177b'

Te pliki będą wyświetlane jak ?zwykle ls:

$ ls
??  ?  ?002  a  abc  b

Ale jeśli lszezwolisz na tę -Qopcję, powinno być jasne, jaka jest prawdziwa nazwa pliku:

$ ls -1iQ *
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"
26739118 "a"
26739121 "a\177b"
26739120 "abc"
26739119 "b"

są to numery i-węzłów i „Cytowane” nazwy w 1kolumnie plików w pwd.

Oznacza to, że używa możliwości powłoki (POSIX) do używania rozszerzeń (globbing):

Aby mieć nazwy z dowolnym znakiem niedrukowalnym:

$ ls -1iQ *[![:print:]]*
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"
26739121 "a\177b"

Aby wyświetlić listę plików, które mogą mieć tylko znaki niedrukowalne:

$ ls -1iQ [![:print:]]*
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"

Następnie, aby selektywnie ( -iopcja (interaktywna)) je usunąć, użyj:

$ rm -i [![:print:]]*
rm: remove regular file ''$'\001\002'? n
rm: remove regular file ''$'\033\027'? n
rm: remove regular file ''$'\177'? n

Musisz tylko wybrać, które chcesz usunąć, odpowiadając yna powyższe pytanie.


0

Jeśli masz bash z autouzupełnianiem, może to działać dla ciebie. Wpisałem znak pokazany na liście katalogów. Następnie uruchomiłem autouzupełnianie i przemianowałem rzecz na coś do wydrukowania.

Oto, co zadziałało dla mnie:

$ ll
-rw-r--r--.  1 xxxxxxxx  xxxxx 25608 Oct 11 11:11 ?
$ mv ?  
(hit the [tab] and it gets extended to ...)
$ mv ^[
(now complete the command and rename to something printable)
$ mv ^[ weirdchar
$ ll
-rw-r--r--.  1 xxxxxxxx  xxxxx 25608 Oct 11 11:11 weirdchar
(now delete)
$ rm weirdchar

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.