Comment puis-je supprimer un fichier dont le nom de fichier contient des caractères non imprimables

46

J'ai réussi à créer un fichier qui ne semble pas avoir un nom de fichier. J'ai trouvé des informations sur la façon d'obtenir plus de détails sur le fichier dans le fil suivant .

Cependant, j'ai essayé certaines des suggestions énumérées et n'arrive pas à supprimer le fichier. Je ne suis pas sûr de ce que j'ai fait pour le créer, mais c'est ce qui est arrivé en essayant de copier un fichier XML.

Quelques informations sur le fichier sont les suivantes:

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

> file *
:               XML document

> ls -i
 417777   

J'ai essayé de trouver l'aide du commutateur inum, puis de le diriger, car cela semblait être le moyen le plus sûr de s'en débarrasser. Cependant, l'exemple donné au bas du fil de discussion lié ci-dessous a échoué pour moi. Exemple:

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

J'ai donc d'abord essayé d'utiliser la liste de chemins comme ci-dessous, mais cela n'a pas fonctionné non plus:

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

Je ne sais pas quel est le caractère \ 177 non imprimable ni comment je peux le transmettre à une rmcommande, mais je veux vraiment m'assurer de ne pas gâcher d'autres fichiers / répertoires dans ma tentative de suppression de ce fichier.

M. Moose
la source
1
\177est le DELcaractère ASCII .
Keith Thompson
9
417777! = 41777
un CVn
Excellent point Michael. C'est probablement pourquoi ma commande n'a jamais fonctionné.
M. Moose
@KeithThompson Est-ce octal sans un chef 0?
chat
1
@cat: Dans ce contexte, oui. Il suit la syntaxe C pour les littéraux de chaîne et de caractères.
Keith Thompson

Réponses:

46

Le fichier a un nom, mais il est composé de caractères non imprimables. Si vous utilisez ksh93, bash, zsh, mksh ou FreeBSD sh, vous pouvez essayer de le supprimer en spécifiant son nom non imprimable. Assurez-vous d'abord que le nom est correct avec: ls -ld $'\177' S'il affiche le bon fichier, utilisez ensuite rm:rm $'\177'

Une autre approche (un peu plus risquée) consiste à utiliser rm -i -- *. Avec l'option -i, rm requiert une confirmation avant de supprimer un fichier. Vous pouvez ainsi ignorer tous les fichiers que vous souhaitez conserver, sauf celui-ci.

Bonne chance!

antje-m
la source
1
Je soupçonne que cela rm -i *n'aurait pas fonctionné dans ce cas; à cause des nouvelles lignes, le shell serait étendu *à quelque chose qui rmne serait pas reconnu comme nom de fichier.
Keith Thompson
6
@ KeithThompson: l'expansion de Glob dans la coque n'est pas sujette à interprétation. Il n'y aurait pas de nouvelle ligne dans le nom développé à moins qu'il n'en contienne un.
Christoffer Hammarström le
@ ChristofferHammarström: Hmm. Je vais faire quelques expériences et commenter plus loin.
Keith Thompson le
@ ChristofferHammarström: Il y a une nouvelle ligne dans le nom développé car le nom du fichier contient des caractères de nouvelle ligne. Mais il semble que j'ai sous bash- estimé et GNU rm. Sur mon système, rm *, rm -i *et rm Ctrl-V DEL TAB ENTERtout fonctionne correctement, même si le nom de fichier contient un caractère de nouvelle ligne (je "\177foo\nbar\n"). Certaines commandes ne gèrent pas bien ces noms de fichiers, mais les outils GNU semblent robustes; les versions non GNU des mêmes outils pourraient ne pas se comporter aussi bien. Dans tous les cas, il est utile de connaître les différentes astuces de ces réponses.
Keith Thompson
1
@ KeithThompson, n'importe quel shell gérera correctement le fichier globbing, pas seulement Bash, et rmne fera jamais aucune forme de fractionnement de mots, qu'il s'agisse de GNU rmou non. Le shell développe les globs et, lorsqu'il exécute la commande appelée, il transmet les noms de fichiers résultants sous la forme d'arguments séparés par une valeur NULL (dans le code C). Ce qui est dangereux, c'est de diriger une liste de fichiers vers quelque chose qui utilise des nouvelles lignes comme délimiteurs; see Pourquoi la boucle sur la sortie de find est-elle une mauvaise pratique?
Wildcard
23

Pour ceux qui l'utilisent, vimlancez-le dans le répertoire de travail actuel:

$ vim ./ 

et naviguez jusqu'au fichier avec les flèches ou j/k. Appuyez ensuite sur Shift+Det confirmez la suppression avec y.


la source
j'ai généralement peur de vim, mais cela a très bien fonctionné
tofutim
9

Il existe probablement un moyen de transmettre le nom de fichier à rm, mais si vous craignez de perdre quoi que ce soit, vous pouvez utiliser un gestionnaire de fichiers à interface graphique. Emacs est livré avec un mode d’édition de répertoire que vous pouvez utiliser si vous l’avez installé:

  1. Ouvrez le dossier dans emacs. Vous pouvez exécuter emacs /path/to/folderou ouvrir emacs, appuyer sur Esc+ xet exécuter dired, ce qui vous demandera le chemin

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

  2. Accédez à la ligne contenant le fichier à supprimer et appuyez sur d. Vous devriez voir un Ddans la marge de gauche à côté du fichier:

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

  3. Appuyez sur xpour enregistrer vos modifications. Cela vous demandera de vous assurer que vous voulez supprimer le fichier; appuyez sur y:

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

Michael Mrozek
la source
1
Cela semble être une bonne idée, mais je n'arrive pas à trouver Emacs sur cette machine. Au risque de déclencher une guerre sainte ... cela peut-il être fait avec vim?
M. Moose
2
Pour ceux qui l'utilisent, vimexécutez-le dans le répertoire de travail actuel: vim ./et naviguez jusqu'au fichier à l'aide des touches fléchées. Appuyez ensuite sur Shift+Det confirmez la suppression avec y.
@hesse Wow. Ce n’est pas quelque chose que j’aurais espéré vimsoutenir
Michael Mrozek
1
@MichaelMrozek a accepté, il a toutes ces caractéristiques que j'ai tendance à découvrir.
1
@Kevin Mode nyan: nyan-mode.buildsomethingamazing.com
Tim
8

Vous avez déjà démontré avec file *le shell que glob ( *) est capable de récupérer ce fichier, donc maintenant faites un rmglob sur ce glob.

  • Passez au répertoire dans lequel se trouve le fichier.
  • Courir rm -i *
  • Entrez npour tous les fichiers sauf celui avec le nom de fichier apparemment invisible
Patrick
la source
7

Le fichier a en fait un nom, mais il est composé de caractères non imprimables.

Une méthode simple consiste à s’appuyer sur l’achèvement du shell: appuyez sur Tabjusqu'à ce que le nom de fichier «étrange» soit inséré. Votre shell doit être configuré pour basculer entre les achèvements possibles:

  • Dans bash, vous devez appeler menu-complete, qui n'est pas lié par défaut, mais completequi est lié à Tabpar défaut. Vous pouvez également utiliser Esc *des insertions pour tous les fichiers et supprimer ceux que vous ne souhaitez pas supprimer de la ligne de commande.
  • Dans zsh, les paramètres par défaut conviennent. vous devez avoir setopt auto_menuou setopt menu_completedéfinir.

Si l'achèvement n'est pas utile dans votre shell, vous pouvez appeler rm -i -- *et répondre «non» à l'exception de ce fichier particulier. Si le nom du fichier ne commence pas par un caractère imprimable, vous pouvez limiter les correspondances aux fichiers dont le premier caractère ne fait pas partie d'un ensemble de caractères imprimables:

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

(Faites attention si votre modèle peut correspondre à un fichier appelé -f, ce qui rmserait considéré comme une option.)

Une autre méthode consiste à appeler ls -ipour trouver l'inode du fichier (un inode identifie de manière unique un fichier sur un système de fichiers donné), puis find -inumà opérer sur ce fichier particulier. Cette méthode est surtout utile lorsque le répertoire contient beaucoup de fichiers.

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

Si vous findn'avez pas d' -deleteoption, appelez rm:

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

(Oui, je sais que la plupart de ces informations ont déjà été publiées. Mais les réponses avec les informations pertinentes compliquent encore les choses.)

Gilles, arrête de faire le mal
la source
4

Essayez d’utiliser echo -epour obtenir le personnage \177, puis xargspour le transmettre à rm. echone prend pas d'échappements décimaux, convertissez-vous donc en hexadécimal: il s'avère que 177c'est un octal; echonécessite un \0(littéral, octet non nul) avant:

echo -ne '\0177\0' | xargs -0 rm
Kevin
la source
Voulez-vous dire que \ xb1 \ 0 est la représentation hexadécimale de \ 177?
M. Moose
@MrMoose \xb1est décimal 177, le \0termine par null et -0dit xargsde prendre un nom terminé par un caractère null au lieu d'un nom avec une terminaison de nouvelle ligne.
Kevin
J'utilise bash sur SunOS et lorsque j'ai essayé votre commande, j'ai reçu xargs: option illégale - 0. J'ai consulté la page de manuel et n'ai pas trouvé d'option pour la résiliation nulle. J'ai réussi à obtenir un correctif pour le problème maintenant cependant. Voir la réponse marquée comme réponse.
M. Moose
Votre echo, tel quel , envoie 2 noms de fichier à xargs, puis à rm... le deuxième nom de fichier est \n.. Il en résulte une erreur ou la suppression d'un fichier de ce nom ('\ n')
Peter. O
@perer Oui, je viens de le remarquer. J'ai ajouté -npour me débarrasser de la nouvelle ligne.
Kevin
3

Je peux juste pour vous garantir que le fichier n'avoir un nom. Il se trouve que c’est un nom qui contient des caractères non imprimables.

Si cela rmn'a pas fonctionné, utiliser le résultat de findl' -inumoption avec comme argument rmne devrait pas vous aider. il va juste passer le même argument à rm, avec le même problème.

Le fichier est le seul dans ce répertoire, non?

La première chose que j'essaierais est d' cdentrer dans le répertoire, puis de taper rm ./puis d'appuyer sur la touche Tab. Cela devrait développer l'argument jusqu'au nom réel du fichier et le précédent ./devrait empêcher rmd'interpréter l'argument comme autre chose qu'un nom de fichier.

Une autre solution consiste à cdutiliser le répertoire parent, puis à utiliser rm -r(ou, si nécessaire rm -rf) sur le répertoire. Cela devrait supprimer le répertoire et tout son contenu, quel que soit son nom. Vous pouvez ensuite utiliser mkdirpour recréer le répertoire (et peut-être chmodpour restaurer ses autorisations).

MODIFIER :

En regardant à ls -lbnouveau votre sortie, je pense que le nom du fichier commence par un DELcaractère ASCII (ce qui est mauvais, pas fatal) et contient un ou plusieurs caractères de nouvelle ligne (ce qui est vraiment mauvais). Autant que je sache, la rmcommande ne peut pas interpréter une nouvelle ligne comme faisant partie d'un nom de fichier.

Mais l' unlink()appel système peut. Voici un script Perl que j’ai concocté et qui va parcourir les fichiers du répertoire courant et vous demander pour chacun d’eux si vous souhaitez le supprimer. Il n'utilise pas de commande externe comme rmpour supprimer les fichiers. Il devrait donc être capable de gérer tous les caractères amusants pris en charge par le système de fichiers (tout ce qui est autre que ASCII NULet /).

Faire rm -rou rm -rfsur le répertoire contenant devrait toujours fonctionner, mais vous pouvez essayer ceci si vous voulez simplement supprimer un seul fichier.

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

(Une autre solution, s'il y a d'autres fichiers dans le répertoire que vous souhaitez conserver, consiste à déplacer tous les autres fichiers dans un répertoire temporaire, puis à supprimer et à recréer le répertoire, puis à déplacer les autres fichiers vers l'arrière.)

(Oh, et quoi que vous ayez fait pour créer ce fichier, essayez de ne pas le refaire!)

Keith Thompson
la source
1

Si vous pouvez trouver exactement ce fichier à l'aide de la findcommande, vous pouvez utiliser le -deleteprédicat. Soyez juste prudent et définissez -deleteun dernier paramètre dans la commande find, sinon cela fera très mal.

Michał Šrajer
la source
1

Tu es proche. Étape par étape, voici comment supprimer le fichier par numéro d'inode.

Tout d’abord, recherchez le numéro d’inode du fichier souhaité à l’aide de ls -li. Le numéro d'inode sera dans la première colonne de sortie.

Par exemple:

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

Dans ce cas, le numéro d'inode est 311010

Utilisez findpour rechercher le fichier par numéro d'inode.

find . -inum 311010 

Assurez-vous que findne renvoie que le nom du fichier que vous souhaitez supprimer. Par exemple:

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

Une fois que vous avez findtrouvé le bon fichier, passez le -deleteparamètre à find. Il supprimera le fichier pour vous.

find . -inum 311010 -delete
Christian Long
la source
0

Les autres réponses sont excellentes et fonctionneront dans presque tous les environnements.

Mais, si vous avez une interface graphique en cours, ouvrez simplement le répertoire dans Nautilus, Dolphin, Konqueror ou votre gestionnaire de fichiers préféré. Vous devriez pouvoir facilement voir et supprimer tout fichier dont le nom ne coopère pas en quelques clics de souris. .

Si vous n'êtes pas un utilisateur régulier d'emacs ou de vim (comme indiqué dans plusieurs autres réponses), c'est peut-être le moyen le plus simple.

Joe
la source
0

Avait un problème similaire avec un fichier nommé "\ 033" (le caractère réel "Escape").

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

Ce qui précède vous invite à supprimer des fichiers du répertoire en cours qui ne commencent pas par une lettre ou un chiffre.

Signal15
la source
0

Au lieu de cela, vous pouvez renommer le répertoire actuel en DirX_OLD, créer un nouveau répertoire avec le nom DirXet déplacer tous les fichiers requis de DirX_OLDvers DirX. Après avoir copié les fichiers, vous pouvez supprimerDirX_OLD

utilisateur106382
la source
0

Juste au cas où cela aiderait quelqu'un, j'ajouterais mes deux cents. J'ai pu obtenir le (s) nom (s) de fichier en utilisant statle caractère générique comme ceci:

stat -c %N *

Sortie:

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

Ensuite, je pourrais utiliser les chaînes citées par ANSI C (telles qu'utilisées dans la réponse acceptée) pour accéder aux fichiers:

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

Chaque fichier a un nom, il vous suffit de trouver exactement lequel.

Je vais utiliser ces fichiers comme exemples:

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

Ces fichiers montreront comme ?d'habitude ls:

$ ls
??  ?  ?002  a  abc  b

Mais si vous lsautorisez l' -Qoption, le nom de fichier réel doit être clairement défini:

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

ce sont les numéros d'inode et les noms "cités" dans la 1colonne des fichiers de pwd.

C'est-à-dire que vous utilisez l'habilité d'un shell (POSIX) pour utiliser des extensions (globbing):

Pour avoir les noms avec n'importe quel caractère non imprimable:

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

Et pour lister les fichiers qui ne peuvent contenir que des caractères non imprimables:

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

Ensuite, pour les -isupprimer sélectivement ( option (interactive)), utilisez:

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

Il vous suffit de choisir ceux à effacer en répondant yà la question ci-dessus.

Isaac
la source
0

Si vous avez une bash avec la complétion automatique, cela peut fonctionner pour vous. J'ai tapé le caractère qui était montré dans la liste de répertoire. Ensuite, j'ai déclenché la saisie semi-automatique et renommé la chose en quelque chose imprimable.

Voici ce qui a fonctionné pour moi:

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

a travaillé pour moi.

Ryan S
la source