Как я могу удалить файл, имя файла которого содержит непечатаемые символы


46

Мне каким-то образом удалось создать файл, который, кажется, не имеет имени файла. Я нашел некоторую информацию о том, как получить более подробную информацию о файле в следующей теме.

Тем не менее, я попробовал некоторые из перечисленных предложений и не могу удалить файл. Я не уверен, что я сделал, чтобы создать его, но это произошло при попытке скопировать XML-файл.

Некоторая информация о файле выглядит следующим образом;

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

> file *
:               XML document

> ls -i
 417777   

Я попытался найти с помощью переключателя inum, а затем передать его rm, так как это казалось самым надежным способом избавиться от него. Тем не менее, пример, приведенный в нижней части темы, связанной ниже, потерпел неудачу для меня. Пример был:

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

поэтому я сначала попытался использовать список путей, как показано ниже, но это тоже не сработало:

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

Я не уверен, что такое непечатаемый символ \ 177 или как я могу передать его rmкоманде, но я действительно хочу убедиться, что я не испортил другие файлы / каталоги при попытке удалить этот файл.


1
\177является DELсимволом ASCII .
Кит Томпсон

9
! 417777 = 41777
CVn

Отличная точка зрения Майкл. Наверное, поэтому моя команда никогда не работала.
Мистер Мус

@KeithThompson Это восьмеричное без ведущего 0?
кот

1
@cat: В этом контексте да. Следует синтаксис C для строковых и символьных литералов.
Кит Томпсон

Ответы:


46

Файл имеет имя, но состоит из непечатаемых символов. Если вы используете ksh93, bash, zsh, mksh или FreeBSD sh, вы можете попытаться удалить его, указав его непечатаемое имя. Сначала убедитесь, что имя верно с: ls -ld $'\177' Если оно показывает правильный файл, затем используйте rm:rm $'\177'

Другой (немного более рискованный) подход заключается в использовании rm -i -- *. С опцией -i rm требует подтверждения перед удалением файла, поэтому вы можете пропустить все файлы, которые хотите сохранить, кроме одного.

Удачи!


1
Я подозреваю, что rm -i *это не сработало бы в этом случае; из-за перевода строки оболочка будет расширяться *до того, rmчто не будет распознаваться как имя файла.
Кит Томпсон

6
@KeithThompson: расширение Glob в оболочке не подлежит дальнейшей интерпретации. В расширенном имени не было бы новой строки, если бы она не содержала его.
Кристофер Хаммарстрем

@ ChristofferHammarström: Хм. Я сделаю несколько экспериментов и прокомментирую дальше.
Кит Томпсон

@ ChristofferHammarström: в расширенном имени есть новая строка, потому что имя файла содержит символы новой строки. Но похоже я недооценил bashи GNU rm. В моей системе rm *, rm -i *и rm Ctrl-V DEL TAB ENTERвсе работают правильно, даже если имя файла содержит символ новой строки (я "\177foo\nbar\n"). Некоторые команды плохо обрабатывают такие имена файлов, но инструменты GNU кажутся надежными; не-GNU версии тех же инструментов могут не работать. В любом случае, различные уловки в этих ответах полезно знать.
Кит Томпсон

1
@KeithThompson, любая оболочка будет правильно обрабатывать глобализацию файлов, не только Bash, и rmникогда не будет разбивать слова на части, будь то GNU rmили нет. Оболочка расширяет глобусы и, когда она выполняет вызванную команду, передает полученные имена файлов как аргументы, разделенные нулями (в коде C). Что опасно, так это передать список файлов чему-то, что использует в качестве разделителей символы новой строки ; см. Почему циклическая обработка вывода find является плохой практикой?
Wildcard

23

Для тех, кто использует, vimзапустите его в текущем рабочем каталоге:

$ vim ./ 

и перейдите к файлу с помощью клавиш со стрелками или j/k. Затем нажмите Shift+Dи подтвердите удаление нажатием y.


я вообще боюсь vim, но это сработало очень хорошо
тофутим

9

Возможно, есть способ передать имя файла rm, но если вы беспокоитесь о том, чтобы что-то испортить, вы можете использовать файловый менеджер с графическим интерфейсом. Emacs поставляется с режимом редактирования каталогов, который вы можете использовать, если он у вас установлен:

  1. Откройте папку в emacs. Вы можете запустить emacs /path/to/folderили открыть emacs, нажать Esc+ xи запустить dired, что запросит путь

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

  2. Переместитесь в строку с файлом, который хотите удалить, и нажмите d. Вы должны увидеть Dслева поле рядом с файлом:

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

  3. Нажмите, xчтобы сохранить изменения. Будет предложено убедиться, что вы хотите удалить файл; нажмите y:

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


1
Выглядит как отличная идея, но я не могу найти emacs на этой машине. На риск начать священную войну ... это можно сделать в VIM?
Мистер Мус

2
Для тех, кто использует, vimзапустите его в текущем рабочем каталоге: vim ./и перейдите к файлу с помощью клавиш со стрелками. Затем нажмите Shift+Dи подтвердите удаление нажатием y.

@ Ух ты. Не то, что я бы ожидал vimподдержать
Майкл Мрозек

1
@MichaelMrozek согласился, в нем есть все эти особенности, которые я иногда обнаруживаю.


8

Вы уже продемонстрировали, file *что shell glob ( *) может подобрать этот файл, так что теперь просто сделайте с rmэтим glob.

  • Перейдите в каталог, в котором находится файл.
  • Бегать rm -i *
  • Введите nдля всех файлов, кроме одного с, казалось бы, невидимым именем файла

7

Файл на самом деле имеет имя, но он состоит из непечатаемых символов.

Простой способ - полагаться на завершение оболочки: нажимайте Tabнесколько раз, пока не будет вставлено «странное» имя файла. Ваша оболочка должна быть настроена на цикл между возможными завершениями:

  • В bash вам нужно вызывать menu-complete, который не связан по умолчанию, а не complete, который связан Tabпо умолчанию. Или используйте Esc *для вставки дополнений для всех файлов и удалите те, которые вы не хотите удалять, из командной строки.
  • В Zsh стандартные настройки хороши; вам нужно иметь setopt auto_menuили setopt menu_completeустановить.

Если завершение не помогает в вашей оболочке, вы можете позвонить rm -i -- *и ответить «нет», за исключением этого конкретного файла. Если имя файла не начинается с печатного символа, вы можете ограничить совпадения файлами, первый символ которых не входит в набор печатных символов:

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

(Будьте осторожны, если ваш шаблон может соответствовать файлу с именем -f, который rmбудет рассматриваться как вариант.)

Другой метод заключается в вызове, ls -iчтобы найти индекс файла (индекс уникально идентифицирует файл в данной файловой системе), а затем find -inumработать с этим конкретным файлом. Этот метод в основном полезен, когда каталог содержит много файлов.

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

Если у вас findнет -deleteвыбора, позвоните rm:

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

(Да, я знаю, что большая часть этого уже была опубликована. Но ответы с соответствующей информацией усложняют ситуацию.)


4

Попробуйте использовать, echo -eчтобы получить персонажа \177, а затем xargsпередать его rm. echoне принимает десятичные побеги, так обращенного в шестнадцатеричном: Выключает 177восьмеричный; echoтребует \0(буквальный, а не нулевой байт) перед:

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

Вы говорите, что \ xb1 \ 0 - это шестнадцатеричное представление \ 177?
Мистер Мус

@MrMoose \xb1является десятичным 177, \0завершает его нулем и -0говорит xargsбрать имя с нулевым символом в конце вместо символа с новой строкой.
Кевин

Я использую bash в SunOS, и когда я попробовал вашу команду, я получил xargs: недопустимый параметр - 0. Я посмотрел на страницу руководства и не увидел опции для нулевого завершения. Мне удалось получить решение этой проблемы сейчас, хотя. Смотрите ответ, помеченный как ответ.
Мистер Мус

Вы echo, как есть, отправляете 2 имени файла в xargs, а затем в rm... второе имя файла \n... Это приводит к ошибке или удалению файла с таким именем ('\ n')
Питер. О

@perer Да, я только что заметил это. Я добавил, -nчтобы избавиться от новой строки.
Кевин

3

Я могу только о гарантии, что файл действительно есть имя. Это просто имя, которое содержит непечатные символы.

Если rmэто не сработало, то использование вывода findс -inumпараметром в качестве аргумента rmвряд ли поможет; он просто передаст тот же аргумент rm, с той же проблемой.

Файл единственный в этом каталоге, верно?

Первое, что я попробую, - это cdзайти в каталог, затем набрать rm ./, а затем нажать клавишу Tab. Это должно расширить аргумент до фактического имени файла, а предшествующее ./должно препятствовать rmинтерпретации аргумента как чего-то отличного от имени файла.

Другое решение заключается в том, чтобы cdперейти в родительский каталог, а затем использовать rm -r(или, если необходимо, rm -rf) каталог. Это должно удалить каталог и все его содержимое, независимо от имени. Затем вы можете использовать, mkdirчтобы воссоздать каталог (и, возможно, chmodвосстановить его разрешения).

РЕДАКТИРОВАТЬ :

Глядя на ваш ls -lbвывод еще раз, я думаю, что имя файла начинается с символа ASCII DEL(что плохо, но не фатально) и содержит один или несколько символов новой строки (что действительно плохо). Насколько я могу судить, rmкоманда не может интерпретировать символ новой строки как часть имени файла.

Но unlink()системный вызов может. Вот сценарий Perl, который я создал вместе, который будет перебирать файлы в текущем каталоге и спрашивать вас, хотите ли вы удалить его. Он не использует внешнюю команду, например, rmдля удаления файлов, поэтому он должен иметь возможность обрабатывать любые забавные символы, которые поддерживает файловая система (что угодно, кроме ASCII NULи /).

Делать rm -rили rm -rfна содержащем каталог должен продолжать работать, но если вы просто хотите , чтобы удалить один файл , вы можете попробовать это.

#!/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;
}

(Другое решение, если в каталоге есть другие файлы, которые вы хотите сохранить, состоит в том, чтобы переместить все остальные файлы во временный каталог, затем разорвать и заново создать каталог и переместить другие файлы обратно.)

(О, и что бы вы ни делали, чтобы создать этот файл, попробуйте не делать это снова!)


1

Если вы можете найти именно этот файл с помощью findкоманды, то вы можете использовать -deleteпредикат. Просто будьте осторожны и сделайте -deleteпоследний параметр команды find, иначе это сильно повредит ...


1

Вы находитесь рядом. Пошагово, вот как удалить файл по номеру инода.

Сначала найдите номер инода нужного вам файла, используя ls -li. Номер инода будет в первом столбце вывода.

Например:

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

В этом случае номер индекса равен 311010

Используйте findдля поиска файла по номеру инода.

find . -inum 311010 

Убедитесь, что findвозвращается только имя файла, который вы хотите удалить. Например:

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

Если вы уверены, что findнашли нужный файл, передайте -deleteпараметр в find. Это удалит файл для вас.

find . -inum 311010 -delete

0

Другие ответы великолепны и будут работать практически во всех средах.

Но, если у вас запущен графический интерфейс, просто откройте каталог в nautilus, dolphin, konqueror или в вашем любимом файловом менеджере, и вы сможете легко увидеть и удалить любой файл с неработающим именем с помощью пары щелчков мыши. ,

Если вы не обычный пользователь emacs или vim (как показано в нескольких других ответах), это может быть самый простой способ.


0

Была похожая проблема с файлом с именем «\ 033» (фактический символ «Escape»).

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

Выше будет предложено удалить файлы в текущем каталоге, которые не начинаются с буквы или цифры.


0

Вместо этого вы можете переименовать текущий каталог в DirX_OLD, создать новый каталог с именем DirXи переместить все необходимые файлы из DirX_OLDв DirX. После копирования файлов вы можете удалитьDirX_OLD


0

На всякий случай, если это кому-нибудь поможет, я прибавлю свои два цента. Мне удалось получить имена файлов, используя statподстановочный знак, как это:

stat -c %N *

Выход:

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

Тогда я мог бы использовать строки ANSI C-кавычки (как в принятом ответе) для доступа к файлам:

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

0

У каждого файла есть имя, вам просто нужно найти, какое именно.

Я буду использовать эти файлы в качестве примеров:

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

Эти файлы будут отображаться как ?обычно ls:

$ ls
??  ?  ?002  a  abc  b

Но если вы lsразрешите -Qопцию, должно быть ясно, каково настоящее имя файла:

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

это номера инодов и имена в кавычках в 1столбце файлов в pwd.

Это означает использование оболочки (POSIX) для использования расширений (globbing):

Чтобы иметь имена с любым непечатным символом:

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

И перечислить файлы, которые могут иметь только непечатаемые символы:

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

Затем, чтобы выборочно ( -i(интерактивная) опция) удалить их, используйте:

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

Вам просто нужно выбрать, какие из них удалить, ответив yна вопрос выше.


0

Если у вас есть bash с автозаполнением, это может сработать для вас. Я набрал символ, который был показан в списке каталогов. Затем я запустил автозаполнение и переименовал вещь в нечто, что можно распечатать.

Вот что сработало для меня:

$ 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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.