web-dev-qa-db-fra.com

Comment détecter si un fichier a une nomenclature UTF-8 dans Bash?

J'essaie d'écrire un script qui supprimera automatiquement les nomenclatures UTF-8 d'un fichier. Je ne parviens pas à détecter si le fichier en a un ou non. Voici mon code:

function has-bom {
    # Test if the file starts with 0xEF, 0xBB, and 0xBF
    head -c 3 "$1" | grep -P '\xef\xbb\xbf'
    return $?
}

Pour une raison quelconque, head semble ignorer la nomenclature devant le fichier. Par exemple, en exécutant cette

printf '\xef\xbb\xbf' > file
head -c 3 file

n'imprimera rien.

J'ai essayé de rechercher une option dans head --help qui me permettrait de contourner ce problème, mais sans succès. Y a-t-il quelque chose que je puisse faire pour que cela fonctionne?

17
James Ko

Commençons par démontrer que head fonctionne réellement correctement:

$ printf '\xef\xbb\xbf' >file
$ head -c 3 file 
$ head -c 3 file | hexdump -C
00000000  ef bb bf                                          |...|
00000003

Créons à présent une fonction has_bom. Si votre grep supporte -P, une option est:

$ has_bom() { head -c3 "$1" | LC_ALL=C grep -qP '\xef\xbb\xbf'; }
$ has_bom file && echo yes
yes

Actuellement, seul GNU grep prend en charge -P.

Une autre option consiste à utiliser le $'...' de bash:

$ has_bom() { head -c3 "$1" | grep -q $'\xef\xbb\xbf'; }
$ has_bom file && echo yes
yes

ksh et zsh prennent également en charge $'...', mais cette construction n'est pas POSIX et dash ne la prend pas en charge.

Remarques:

  1. L'utilisation d'un return $? explicite est facultative. La fonction retournera par défaut avec le code de sortie de la dernière commande exécutée.

  2. J'ai utilisé le formulaire POSIX pour définir des fonctions. Cela équivaut à la forme bash mais vous pose un problème en moins si vous devez exécuter la fonction sous un autre shell.

  3. bash accepte l'utilisation du caractère - dans un nom de fonction, mais il s'agit d'une fonctionnalité controversée. Je l'ai remplacé par _ qui est plus largement accepté. (Pour plus d'informations sur cette question, voir cette réponse .)

  4. L'option -q de grep le rend silencieux, ce qui signifie qu'il définit toujours un code de sortie correct, mais n'envoie aucun caractère à stdout.

16
John1024

J'ai appliqué ce qui suit pour la première ligne de lecture:

read c
if (( "$(printf "%d" "'${c:0:1}")" == 65279 ))  ; then c="${c:1}" ; fi

Cela supprime simplement la nomenclature de la variable.

0
apexik