Linux / Commande file : identifier le type et l’encodage d’un fichier

file estime le type d’un fichier à partir des métadonnées et du contenu, sans se fier uniquement à son extension. Utilisez file chemin pour une description, file -b --mime-type chemin pour le type MIME et file -b --mime-encoding chemin pour l’encodage estimé.

Cette classification aide à comprendre un téléchargement sans extension, un script ou des données mal nommées. Elle ne prouve ni qu’un fichier est intact, ni qu’il est sûr à ouvrir ou à exécuter. Les résultats dépendent notamment de la version de file, de sa base de motifs et des octets effectivement examinés.

Comprendre ce que file examine

Le programme applique normalement plusieurs familles de tests, dans cet ordre :

  1. Informations du système de fichiers. Il peut reconnaître un répertoire, un lien symbolique, un fichier vide ou un fichier spécial à partir des métadonnées.
  2. Motifs magic. Une base de règles décrit des octets et des structures caractéristiques, parfois à des positions précises dans le fichier. Ces règles identifient notamment des images, archives et exécutables.
  3. Texte et langage. Le programme recherche des caractéristiques d’encodage et des indices de contenu, par exemple un texte ou un script.

Le premier test concluant fournit normalement le résultat. -k demande de poursuivre au-delà de la première correspondance, mais plusieurs descriptions ne constituent pas une validation complète du format. Les règles magic ne se limitent pas à quelques octets fixes au début de tous les fichiers.

Un résultat data indique que le contenu n’a pas été reconnu plus précisément par les tests appliqués. Il ne démontre pas que le fichier est corrompu. Inversement, un en-tête reconnaissable peut subsister dans un fichier tronqué ou trompeur.

Le manuel officiel de file décrit cet ordre de tests et les options. La documentation du format magic explique la base de règles.

Lancer une inspection et interpréter la description

file --version
file --help
file -- ./README.md './rapport annuel.pdf'
file -- ./-exemple.bin

Les fichiers doivent exister. Les guillemets conservent les espaces, tandis que -- termine les options. Le préfixe ./ indique un nom relatif explicite ; il permet aussi de distinguer un fichier nommé exactement - de la lecture de l’entrée standard.

La sortie habituelle contient le chemin, un séparateur et une description. Les mots techniques restent souvent en anglais, même dans un terminal configuré en français :

Description rencontrée Interprétation utile
directory Le chemin désigne un répertoire.
symbolic link to Le programme décrit un lien vers un autre chemin.
empty Fichier ordinaire vide.
ASCII text Texte compatible avec les caractères ASCII reconnus.
Unicode text, UTF-8 Texte identifié comme encodé en UTF-8.
ELF Format Executable and Linkable Format, utilisé notamment pour des programmes et bibliothèques sous Linux.
data Données que les tests n’ont pas classées plus précisément.
cannot open Un accès au chemin a échoué ; ce n’est pas un type de document.

Une mention d’architecture ou de liaison dynamique aide à choisir le contexte d’exécution, mais ne garantit pas qu’un programme pourra fonctionner sur votre machine. L’interpréteur, les bibliothèques, les permissions et le système d’exploitation doivent aussi convenir. Ne lancez pas le fichier uniquement pour confirmer le résultat de file.

Lire pour classifier ne réécrit normalement pas les données du fichier. La lecture peut cependant modifier sa date d’accès selon le système de fichiers et les options de montage. -p tente de préserver cette date sur les systèmes qui le permettent ; ce n’est pas une promesse de conservation de toutes les métadonnées.

Comparer extension, contenu et encodage dans un exercice

Créer des fichiers de démonstration connus

Dans un répertoire où demo-file n’existe pas, ce bloc Bash crée de petits fichiers. Il nécessite gzip et un script ou terminal utilisant UTF-8 pour conserver les accents de l’exemple :

(
  mkdir -- demo-file || exit 1
  printf '%s\n' 'Bonjour' > demo-file/ascii.txt
  printf '%s\n' 'Bonjour, été' > demo-file/utf8.txt
  printf '%s\r\n' 'Bonjour' > demo-file/crlf.txt
  : > demo-file/vide
  cp -- demo-file/ascii.txt demo-file/fausse-image.jpg
  gzip -c -- demo-file/ascii.txt > demo-file/ascii.txt.gz
)

Le premier mkdir refuse de mélanger l’exercice avec un dossier existant. La commande gzip écrit une version compressée distincte et conserve ascii.txt. Le contenu de fausse-image.jpg reste du texte : seule sa copie porte une extension d’image.

file -- ./demo-file/ascii.txt ./demo-file/utf8.txt ./demo-file/crlf.txt
file -- ./demo-file/vide ./demo-file/fausse-image.jpg

Le premier fichier doit être reconnu comme texte ASCII, le second comme texte UTF-8 si les accents ont été enregistrés dans cet encodage. Le troisième contient les fins de ligne CRLF, ou retour chariot suivi d’un saut de ligne ; file peut les signaler. Le fichier vide est distingué du texte non vide.

La copie .jpg doit rester identifiée comme du texte, ce qui illustre l’intérêt de lire le contenu. La commande ne renomme pas les fichiers et ne convertit pas leur format. Après inspection, vous pouvez retirer les seuls fichiers et dossiers de cet exercice ; les parenthèses n’en effacent pas la création.

Lire le type MIME et l’encodage séparément

MIME signifie Multipurpose Internet Mail Extensions. Des valeurs comme text/plain ou image/png servent à désigner une catégorie de contenu :

file -i -- ./demo-file/utf8.txt
file -b --mime-type -- ./demo-file/utf8.txt
file -b --mime-encoding -- ./demo-file/utf8.txt

-i, synonyme de --mime, affiche ensemble le type et un charset. --mime-type conserve le type ; --mime-encoding conserve l’encodage. Avec -b, le chemin n’est pas ajouté au résultat. Pour le petit texte accentué de l’exercice, les deux dernières valeurs attendues sont text/plain et utf-8.

Un texte ASCII est aussi compatible avec UTF-8 ; un résultat us-ascii ne prouve donc pas que son auteur avait choisi un autre encodage. De courtes suites d’octets peuvent correspondre à plusieurs interprétations. Pour un binaire, charset=binary ne constitue pas un encodage de texte à convertir.

Choisir le traitement des liens et des données compressées

Décrire un lien ou son fichier cible

file -h -- ./lien-vers-document
file -L -- ./lien-vers-document

Ces deux formes alternatives supposent un lien existant. -h, ou --no-dereference, décrit le lien lui-même. -L, ou --dereference, suit le lien pour examiner sa cible. Un lien cassé ne fournit pas de contenu cible à classifier.

Le comportement par défaut peut dépendre de POSIXLY_CORRECT. Dans un script, indiquez explicitement l’option correspondant au besoin. L’explication des chemins logiques et physiques avec cd aide à comprendre pourquoi un nom de lien et sa destination ne sont pas interchangeables.

Distinguer compression et contenu interne

file -- ./demo-file/ascii.txt.gz
file -z -- ./demo-file/ascii.txt.gz
file -Z -- ./demo-file/ascii.txt.gz

La première commande identifie normalement le conteneur gzip. -z tente aussi d’inspecter le contenu décompressé pour les formats pris en charge. -Z privilégie la description du contenu interne sans répéter celle de la compression. L’exemple doit retrouver la nature textuelle du petit contenu connu.

Cette inspection n’équivaut pas à une extraction générale de toutes les entrées d’une archive ni à un contrôle antivirus. Le support dépend de la compilation et des décompresseurs disponibles. Le travail peut utiliser davantage de temps et de mémoire ; choisissez des données connues et une portée adaptée.

Le manuel décrit aussi -S comme une désactivation du sandbox système lorsqu’il existe. Un besoin de décompresseur externe ne justifie pas de l’ajouter automatiquement : examinez les capacités de votre installation et le contexte des données. Les exemples ci-dessus ne demandent pas de désactiver cette protection.

Inspecter plusieurs fichiers sans analyser des lignes ambiguës

Utiliser une liste de chemins ou find -exec

file -- ./demo-file/*.txt
find ./demo-file -maxdepth 2 -type f -exec file -E -- {} +

Le premier motif est développé par Bash. Un nom commençant par un point est généralement omis, un motif sans correspondance peut être transmis littéralement et une très grande liste peut dépasser la limite d’arguments. La commande find limite ici le parcours à deux niveaux et regroupe les chemins sans les découper sur leurs espaces.

Le format humain de sortie reste une présentation : transmettre correctement les arguments ne suffit pas à rendre ensuite ses lignes faciles à analyser. Le guide des noms spéciaux et de ls explique notamment pourquoi une ligne n’est pas un séparateur universel pour les noms Linux.

Pour un fichier de liste contenant un chemin par ligne, utilisez -f. Vous pouvez en préparer un nouveau à côté de l’exercice :

(
  set -o noclobber
  printf '%s\n' './demo-file/ascii.txt' './demo-file/utf8.txt' > ./demo-file-liste.txt
)
file -E -N -F ' => ' -f ./demo-file-liste.txt

-N enlève l’alignement des noms et -F remplace le séparateur. Ces options sont placées avant -f, car la liste est traitée dès que file rencontre cette option. Un fichier de liste par lignes ne convient pas à des noms contenant eux-mêmes un saut de ligne.

Lire des paires séparées par NUL

Avec un seul -0, file ajoute NUL après le nom mais conserve encore sa présentation et son séparateur. Avec deux -0, il produit une paire nom NUL description NUL pour chaque entrée. Ce bloc Bash complet lit ces paires :

(
  set -o pipefail
  file -E -r -0 -0 -- ./demo-file/ascii.txt ./demo-file/utf8.txt |
    while IFS= read -r -d '' nom; do
      if ! IFS= read -r -d '' description; then
        printf '%s\n' 'Enregistrement incomplet.' >&2
        exit 1
      fi
      printf 'Nom : %q ; type : %q\n' "$nom" "$description"
    done
)

-r demande les caractères bruts plutôt que leur représentation en octal. Chaque lecture utilise NUL comme séparateur, tandis que IFS= et read -r conservent les espaces et barres obliques inverses. printf '%q' présente ensuite les valeurs de façon protégée pour Bash.

pipefail conserve un échec du producteur, même si la boucle finit normalement ; la seconde lecture repère une paire incomplète. Ce format de sortie ne transforme pas -f en lecteur de listes NUL. Vérifiez toujours les formats attendus à l’entrée et à la sortie de chaque outil.

Distinguer erreur d’accès et résultat de classification

Sans -E, des problèmes comme un chemin absent peuvent être présentés comme du texte de résultat sans provoquer le statut d’échec que votre script attend. Il est donc insuffisant de tester seulement qu’une commande file ordinaire a renvoyé zéro.

file -- ./demo-file/absent
printf 'Statut sans -E : %s\n' "$?"
file -E -- ./demo-file/absent
printf 'Statut avec -E : %s\n' "$?"

Utilisez un nom réellement absent pour comparer ces appels. L’option -E demande que ces erreurs de système de fichiers deviennent des erreurs signalées avec un statut non nul. Examinez aussi les diagnostics ; ne prenez pas une description comme cannot open pour un type valide.

Le texte officiel du manuel et sa section sur les statuts détaille les conditions de retour. La portée de lecture et l’état du fichier peuvent changer pendant le contrôle : un diagnostic n’est pas une garantie sur un accès futur au même chemin.

Écrire un script de classement MIME avec contrôle d’erreur

Enregistrez ce script complet dans un nouveau fichier classer-image.sh. Il classe un fichier ordinaire parmi trois types d’images prévus ou une autre catégorie :

#!/usr/bin/env bash
if [ "$#" -ne 1 ] || [ -z "$1" ]; then
  printf '%s\n' 'Usage : bash classer-image.sh chemin' >&2
  exit 64
fi

chemin=$1
case $chemin in
  /*) ;;
  *) chemin=./$chemin ;;
esac

if [ ! -f "$chemin" ]; then
  printf '%s\n' 'Un fichier ordinaire accessible est attendu.' >&2
  exit 66
fi

if mime=$(file -E -L -b --mime-type -- "$chemin"); then
  case $mime in
    image/jpeg|image/png|image/webp)
      printf 'Categorie image retenue : %s\n' "$mime"
      ;;
    *)
      printf 'Autre categorie : %s\n' "$mime" >&2
      exit 1
      ;;
  esac
else
  statut=$?
  printf '%s\n' 'La classification a echoue.' >&2
  exit "$statut"
fi

Vous pouvez vérifier le refus de la fausse image de l’exercice, puis essayer une vraie image de votre choix :

bash classer-image.sh ./demo-file/fausse-image.jpg
bash classer-image.sh ./photo.webp

Le premier appel doit signaler une autre catégorie et retourner 1. Le second ne réussit que si photo.webp existe, est un fichier ordinaire et est reconnue dans la liste. Le script utilise 64 pour un mauvais nombre d’arguments ou un chemin vide, 66 pour une cible qui ne satisfait pas son test de fichier et conserve le statut d’une erreur de classification.

Le préfixe ./ empêche qu’un nom relatif égal à - soit lu comme l’entrée standard. Le test de fichier ordinaire suit les liens, tout comme l’option explicite -L de file ; il s’agit d’un choix de ce script, pas d’un refus général des liens. Les guillemets conservent les espaces.

Ce programme trie des fichiers pour un usage local. Pour une application de réception de fichiers, il faut aussi définir les limites de taille, le décodage accepté, le stockage et les autorisations prévus par l’application. Une classification MIME seule ne valide pas une image, ne supprime pas des données supplémentaires et ne ferme pas les courses entre un contrôle et une ouverture ultérieure.

Choisir la suite face à un résultat inattendu

  • Le fichier manque ou l’accès est refusé. Vérifiez le chemin, les droits attendus et le traitement des liens avant de modifier quoi que ce soit. Un répertoire n’est pas un document ordinaire.
  • L’encodage est ambigu. Examinez l’application d’origine et la spécification du fichier. Ne convertissez pas automatiquement un binaire ou un texte court sur la seule suggestion de file.
  • La sortie reste data. Vérifiez la taille, la version et la base magic ; consultez le logiciel ou le format d’origine. Le mot data n’établit pas à lui seul une corruption.
  • Une archive ou un périphérique réclame une autre inspection. Choisissez une méthode adaptée au type de données. -s autorise la lecture de certains fichiers spéciaux ; ne l’ajoutez pas à un parcours général et ne pointez pas un périphérique pour un simple exercice.

Pour examiner un flux connu sans créer de fichier, printf '%s\n' 'Bonjour' | file - lit les données sur l’entrée standard. Un flux n’offre pas nécessairement les mêmes possibilités de repositionnement qu’un fichier, ce qui peut réduire certains détails de classification. Une lecture sur l’entrée standard et une inspection du fichier littéral ./- répondent donc à deux besoins distincts.