¿Cómo puedo eliminar un archivo cuyo nombre de archivo tiene caracteres que no se imprimen?

46

De alguna manera logré crear un archivo que no parece tener un nombre de archivo. Encontré información sobre cómo obtener más detalles del archivo en el siguiente hilo.

Sin embargo, probé algunas de las sugerencias enumeradas y parece que no puedo eliminar el archivo. No estoy seguro de lo que hice para crearlo, pero sucedió al intentar copiar un archivo xml.

Alguna información sobre el archivo es la siguiente;

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

> file *
:               XML document

> ls -i
 417777   

Traté de encontrar usando el interruptor de inum y luego conectarlo a rm, ya que parecía la forma más infalible de deshacerse de él. Sin embargo, el ejemplo dado en la parte inferior del hilo vinculado a continuación falló para mí. Ejemplo fue:

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

Así que intenté usar la lista de rutas primero como la siguiente, pero tampoco funcionó:

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

No estoy seguro de cuál es el carácter no imprimible \ 177 o cómo puedo pasar eso a un rmcomando, pero realmente quiero asegurarme de no estropear ningún otro archivo / directorio en mi intento de eliminar este archivo.

Señor alce
fuente
1
\177es el DELcaracter ASCII .
Keith Thompson el
99
417777! = 41777
un CVn
Excelente punto Michael. Probablemente por eso mi comando nunca funcionó.
Mr Moose
@KeithThompson ¿Es octal sin una guía 0?
gato
1
@cat: En este contexto, sí. Sigue la sintaxis de C para cadenas y literales de caracteres.
Keith Thompson el

Respuestas:

46

El archivo tiene un nombre, pero está hecho de caracteres no imprimibles. Si usa ksh93, bash, zsh, mksh o FreeBSD sh, puede intentar eliminarlo especificando su nombre no imprimible. Primero asegúrese de que el nombre sea correcto con: ls -ld $'\177' Si muestra el archivo correcto, luego use rm:rm $'\177'

Otro enfoque (un poco más arriesgado) es usar rm -i -- *. Con la opción -i, rm requiere confirmación antes de eliminar un archivo, por lo que puede omitir todos los archivos que desea conservar excepto el uno.

¡Buena suerte!

antje-m
fuente
1
Sospecho que eso rm -i *no hubiera funcionado en este caso; debido a las nuevas líneas, el shell se expandiría *a algo que rmno reconocería como el nombre del archivo.
Keith Thompson el
66
@KeithThompson: la expansión de Glob en el shell no está sujeta a una mayor interpretación No habría una nueva línea en el nombre expandido a menos que contuviera una.
Christoffer Hammarström
@ ChristofferHammarström: Hmm. Haré algunos experimentos y comentaré más.
Keith Thompson el
@ ChristofferHammarström: hay una nueva línea en el nombre expandido porque el nombre del archivo contiene caracteres de nueva línea. Pero parece que he subestimado bashy GNU rm. En mi sistema, rm *, rm -i *, y rm Ctrl-V DEL TAB ENTERtodo el trabajo correctamente, incluso si el nombre de archivo contiene un carácter de nueva línea (utilicé "\177foo\nbar\n"). Algunos comandos no manejan bien tales nombres de archivo, pero las herramientas GNU parecen robustas; Las versiones no GNU de las mismas herramientas podrían no funcionar también. En cualquier caso, los diversos trucos en estas respuestas son útiles para conocer.
Keith Thompson el
1
@KeithThompson, cualquier shell manejará el bloqueo de archivos correctamente, no solo Bash, y rmnunca hace ninguna forma de división de palabras, ya sea GNU rmo no. El shell expande los globos y, cuando ejecuta el comando solicitado, pasa los nombres de archivo resultantes como argumentos separados por nulos (en el código C). Lo peligroso es canalizar una lista de archivos a algo que use líneas nuevas como delimitadores; ver ¿Por qué es un bucle sobre la salida de find una mala práctica?
Comodín el
23

Para quienes lo usan, vimejecútelo en el directorio de trabajo actual:

$ vim ./ 

y navegue hasta el archivo con las teclas de flecha o j/k. Luego presione Shift+Dy confirme la eliminación con y.


fuente
Generalmente me da miedo vim, pero eso funcionó muy bien
tofutim
9

Probablemente haya una forma de pasar el nombre de archivo rm, pero si le preocupa desordenar algo, puede usar un administrador de archivos GUI. Emacs viene con un modo de edición de directorio que puede usar si lo tiene instalado:

  1. Abre la carpeta en emacs. Puede ejecutar emacs /path/to/foldero abrir emacs, presionar Esc+ xy ejecutar dired, lo que solicitará la ruta

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

  2. Muévase a la fila con el archivo que desea eliminar y presione d. Debería ver un Den el margen izquierdo al lado del archivo:

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

  3. Presione xpara guardar sus cambios. Se le pedirá que se asegure de que desea eliminar el archivo; prensa y:

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

Michael Mrozek
fuente
1
Parece una gran idea, pero parece que no puedo encontrar emacs en esta máquina. A riesgo de comenzar una guerra santa ... ¿se puede hacer esto en vim?
Mr Moose
2
Para quienes lo usan, vimejecútelo en el directorio de trabajo actual: vim ./y navegue hasta el archivo con las teclas de flecha. Luego presione Shift+Dy confirme la eliminación con y.
@hesse Wow. No es algo que hubiera esperado vimapoyar
Michael Mrozek
1
@MichaelMrozek estuvo de acuerdo, tiene todas estas características que en ocasiones tiendo a descubrir.
1
@Kevin nyan mode: nyan-mode.buildsomethingamazing.com
Tim
8

Ya ha demostrado con file *que el shell glob ( *) es capaz de recoger este archivo, así que ahora solo haga un rmen ese glob.

  • Cambie al directorio en el que se encuentra el archivo.
  • correr rm -i *
  • Ingrese npara todos los archivos excepto el que tiene el nombre de archivo aparentemente invisible
Patricio
fuente
7

El archivo en realidad tiene un nombre, pero está hecho de caracteres no imprimibles.

Un método fácil es confiar en la finalización del shell: presione Tabrepetidamente hasta que se inserte el nombre de archivo "extraño". Debe tener su shell configurado para alternar entre posibles finalizaciones:

  • En bash, debe invocar menu-complete, que no está vinculado de forma predeterminada, en lugar de complete, lo que está vinculado Tabde forma predeterminada. Alternativamente, use Esc *para insertar terminaciones para todos los archivos y elimine los que no desea eliminar de la línea de comandos.
  • En zsh, la configuración predeterminada está bien; necesitas tener setopt auto_menuo setopt menu_completeconfigurar.

Si la finalización no es útil en su shell, puede llamar rm -i -- *y responder "no" a excepción de este archivo en particular. Si el nombre del archivo no comienza con un carácter imprimible, puede restringir las coincidencias a los archivos cuyo primer carácter no está en un conjunto de caracteres imprimibles:

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

(Tenga cuidado si su patrón puede coincidir con un archivo llamado -f, que rmse trataría como una opción).

Otro método es llamar ls -ipara encontrar el inodo del archivo (un inodo identifica de manera única un archivo en un sistema de archivos dado), y luego find -inumoperar en ese archivo en particular. Este método es principalmente útil cuando el directorio contiene muchos archivos.

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

Si findno tiene una -deleteopción, llame al rm:

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

(Sí, sé que la mayor parte de esto ya se ha publicado. Pero las respuestas con la información relevante lo hacen más complicado de lo que debería ser).

Gilles 'SO- deja de ser malvado'
fuente
4

Intenta usar echo -epara obtener el personaje \177y luego xargspasarlo rm. echono toma escapes decimales, por lo que convierte a hexadecimal: Resulta que 177es octal; echorequiere un \0(literal, no byte nulo) antes:

echo -ne '\0177\0' | xargs -0 rm
Kevin
fuente
¿Estás diciendo que \ xb1 \ 0 es la representación hexadecimal de \ 177?
Mr Moose
@MrMoose \xb1es decimal 177, lo \0termina con nulo y -0le dice xargsque tome un nombre con terminación nula en lugar de uno con nueva línea.
Kevin
Estoy usando bash en SunOS y cuando probé su comando, obtuve xargs: opción ilegal - 0. Miré la página del manual y no pude ver una opción para la terminación nula. Sin embargo, logré obtener una solución para el problema ahora. Ver la respuesta marcada como respuesta.
Mr Moose
Su echo, tal cual, envía 2 nombres de archivo a xargs, y luego a rm... el segundo nombre de archivo es \n... Esto da como resultado un error o la eliminación de un archivo con ese nombre ('\ n')
Peter. O
@perer Sí, acabo de notar eso. Agregué -npara deshacerme de la nueva línea.
Kevin
3

Puedo casi garantizarle que el archivo tiene un nombre. Simplemente es un nombre que contiene caracteres no imprimibles.

Si rmno funcionó, entonces usar el resultado de findcon la -inumopción como argumento para rmno es probable que ayude; solo va a pasar el mismo argumento a rm, con el mismo problema.

El archivo es el único en ese directorio, ¿verdad?

Lo primero que probaría es cdingresar al directorio, luego escribir rm ./, luego presionar la tecla Tab. Esto debería expandir el argumento al nombre real del archivo, y lo anterior ./debería evitar rminterpretar el argumento como algo más que un nombre de archivo.

Otra solución es cdusar el directorio padre y luego usar rm -r(o, si es necesario rm -rf) en el directorio. Eso debería eliminar el directorio y todo su contenido, independientemente de su nombre. Luego puede usar mkdirpara recrear el directorio (y tal vez chmodpara restaurar sus permisos).

EDITAR:

Mirando su ls -lbsalida nuevamente, creo que el nombre del archivo comienza con un DELcarácter ASCII (que es malo, no fatal) y contiene uno o más caracteres de nueva línea (que es realmente malo). Por lo que puedo decir, el rmcomando no puede interpretar una nueva línea como parte del nombre de un archivo.

Pero el unlink()sistema puede llamar. Aquí hay un script de Perl que reuní que iterará a través de los archivos en el directorio actual y le preguntará a cada uno si desea eliminarlo. No utiliza un comando externo como rmpara eliminar los archivos, por lo que debería ser capaz de manejar cualquier personaje divertido que admita el sistema de archivos (cualquier cosa que no sea ASCII NULy /).

Hacer rm -ro rm -rfen el directorio que contiene debería funcionar, pero si solo desea eliminar un solo archivo, puede intentarlo.

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

(Otra solución, si hay otros archivos en el directorio que desea mantener, es mover todos los demás archivos a un directorio temporal, luego destruir y recrear el directorio y mover los otros archivos hacia atrás).

(¡Ah, y hagas lo que hayas hecho para crear ese archivo, trata de no volver a hacerlo!

Keith Thompson
fuente
1

Si puede encontrar exactamente ese archivo usando el findcomando, puede usar el -deletepredicado. Solo tenga cuidado y haga -deleteun último parámetro del comando find o le dolerá mucho ...

Michał Šrajer
fuente
1

Estás cerca. Paso a paso, aquí se explica cómo eliminar el archivo por número de inodo.

Primero, encuentre el número de inodo del archivo que desea, utilizando ls -li. El número de inodo estará en la primera columna de salida.

Por ejemplo:

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

En este caso, el número de inodo es 311010

Use findpara buscar el archivo por número de inodo.

find . -inum 311010 

Asegúrese de que findsolo devuelva el nombre del archivo que desea eliminar. Por ejemplo:

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

Una vez que esté seguro de que findestá encontrando el archivo correcto, pase el -deleteparámetro a find. Eliminará el archivo por ti.

find . -inum 311010 -delete
Cristiano largo
fuente
0

Las otras respuestas son geniales y funcionarán en casi todos los entornos.

Pero, si tiene una interfaz gráfica de usuario ejecutándose, simplemente abra el directorio en nautilus, dolphin, konqueror o su administrador de archivos favorito y debería poder ver y eliminar fácilmente cualquier archivo que tenga un nombre no cooperativo con un par de clics del mouse .

Si no eres un usuario habitual de emacs o vim (como aparece en varias otras respuestas), esta podría ser la forma más fácil de hacerlo.

Joe
fuente
0

Tuve un problema similar con un archivo llamado "\ 033" (el carácter real de "Escape").

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

Lo anterior le pedirá que elimine los archivos en el directorio actual que no comienzan con una letra o número.

Señal15
fuente
0

En su lugar, puede cambiar el nombre del directorio actual a DirX_OLD, crear un nuevo directorio con el nombre DirXy mover todos los archivos necesarios de DirX_OLDa DirX. Después de copiar los archivos, puede eliminarlos.DirX_OLD

usuario106382
fuente
0

En caso de que ayude a alguien, agregaré mis dos centavos. Pude obtener el (los) nombre (s) de archivo usando statel comodín de esta manera:

stat -c %N *

Salida:

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

Entonces podría usar cadenas ANSI C entre comillas (como se usa en la respuesta aceptada) para acceder a los archivos:

rm $'\220g\201\asetrans.conf'
miken32
fuente
0

Cada archivo tiene un nombre, solo necesita encontrar exactamente cuál.

Usaré estos archivos como ejemplos:

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

Esos archivos se mostrarán como ?de costumbre ls:

$ ls
??  ?  ?002  a  abc  b

Pero si lspermite la -Qopción, debe quedar claro cuál es el nombre real del archivo:

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

esos son los números de inodo y los nombres "Citados" en la 1columna de los archivos en pwd.

Eso es usar la habilidad de un shell (POSIX) para usar expansiones (globbing):

Para tener los nombres con cualquier carácter que no se imprima:

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

Y para enumerar archivos que pueden tener solo caracteres que no se imprimen:

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

Luego, para -ieliminarlos selectivamente (la opción (interactiva)), use:

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

Solo necesita elegir cuáles borrar borrando yla pregunta anterior.

Isaac
fuente
0

Si tiene una fiesta con autocompletado, esto puede funcionar para usted. Escribí el carácter que se mostró en la lista del directorio. Luego activé el autocompletado y cambié el nombre a algo imprimible.

Esto es lo que funcionó para mí:

$ 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
Sascha
fuente
-4
rm -dfvr /"(null)" 

trabajó para mi.

Ryan S
fuente