Comment recherchez-vous des fichiers contenant des fins de ligne dos (CRLF) avec grep sous Linux?

126

Je veux rechercher des fichiers contenant des fins de ligne dos avec grep sous Linux. Quelque chose comme ça:

grep -IUr --color '\r\n' .

Ce qui précède semble correspondre à un littéral rnqui n'est pas ce qui est souhaité.

La sortie de ceci sera acheminée via xargs dans todos pour convertir crlf en lf comme ceci

grep -IUrl --color '^M' . | xargs -ifile fromdos 'file'
Tim Abell
la source
2
Avez-vous essayé dos2unix ? Il corrige automatiquement les fins de ligne.
sblundy
Je ne suis pas tout à fait sûr mais iirc il y a une différence entre la citation du motif à l'intérieur de 'et ". Afaik dans les motifs inclus dans' les séquences d'échappement sont interprétées comme une chaîne appropriée, donc '\ r' équivaudrait à" \\ r "et" \ r "n'a pas d'équivalent (du moins dans cette notation) avec '.
Anticom
Anticom: Vous avez raison dans ce cas que la différence entre "et" n'est pas pertinente; cependant, elles sont généralement distinctes car "les chaînes entourées sont faiblement entre guillemets et" sont entre guillemets forts. La plus grande chose dont je profite est que les extensions $ ou `` ne se développent pas dans des chaînes entre guillemets faibles. Voir bash-hackers sur les citations pour plus.
bschlueter
4
Le moyen le plus simple est d'utiliser moderne dos2unixavec -icinterrupteur. Pour les fichiers LF, vous pouvez rechercher avec unix2dos -ic. Il ne modifie pas les fichiers. Seulement rapport.
gavenkoa
3
puisqu'il s'agit d'une réponse de premier ordre à toute question concernant les fins de ligne Windows / retours chariot sous Linux, je pense qu'il vaut la peine de noter que vous pouvez les voir dans le terminal avec la commande cat -v somefile.txt; ils apparaissent comme^M
user5359531

Réponses:

121

Utilisez Ctrl+ V, Ctrl+ Mpour entrer un caractère de retour chariot littéral dans votre chaîne grep. Alors:

grep -IUr --color "^M"

fonctionnera - s'il ^My a un CR littéral que vous entrez comme je l'ai suggéré.

Si vous voulez la liste des fichiers, vous souhaitez également ajouter l' -loption.

Explication

  • -I ignorer les fichiers binaires
  • -Uempêche grep de supprimer les caractères CR. Par défaut, il le ferait s'il décide qu'il s'agit d'un fichier texte.
  • -r lire tous les fichiers sous chaque répertoire de manière récursive.
pjz
la source
3
Comme un hack rapide qui fonctionnerait, mais je pense que la solution de lecture humaine serait: grep $ '\ r' / bash shell uniquement / ou grepprintf '\r'
akostadinov
5
@akostadinov +1, mais se est interprété contre - apostrophes de votre commentaire;) La deuxième option, autrement dit, être grep $(printf '\r'). Mais pour la plupart des utilisations pratiques impliquant bash, je m'en tiens à $'\r'.
jankes
3
Remarque: L'option -Un'est pertinente que pour Windows (ou cygwin), mais elle est essentielle ici. Sous Windows, la commande ne fonctionnera pas sans elle.
sleske
3
Quel est le point d'option -I? D'après le manuel, il me semble que les fichiers binaires sont considérés comme non correspondants. La combinaison de -Iet -U(qui applique le type binaire) ne devrait-elle pas avoir pour résultat que tous les fichiers sont considérés comme non correspondants?
Jānis Elmeris
3
Vous mentionnez le drapeau «-l» comme option complémentaire, mais je pense qu'il devrait être inclus dans la réponse principale car la question demande essentiellement une liste de fichiers. En outre, il en résulte une recherche plus rapide.
arr_sea
168

grep n'est probablement pas l'outil que vous souhaitez pour cela. Il imprimera une ligne pour chaque ligne correspondante dans chaque fichier. À moins que vous ne vouliez, par exemple, exécuter todos 10 fois sur un fichier de 10 lignes, grep n'est pas la meilleure façon de procéder. En utilisant find pour exécuter un fichier sur chaque fichier de l'arborescence, puis en parcourant celui de "CRLF", vous obtiendrez une ligne de sortie pour chaque fichier qui a des fins de ligne de style dos:

find . -not -type d -exec file "{}" ";" | grep CRLF

vous obtiendrez quelque chose comme:

./1/dos1.txt: ASCII text, with CRLF line terminators
./2/dos2.txt: ASCII text, with CRLF line terminators
./dos.txt: ASCII text, with CRLF line terminators
Thomee
la source
J'avais déjà craqué ça, mais merci quand même. grep -IUrl --color '^M' . | xargs -ifile fromdos 'file'
Tim Abell
5
L'option -l de grep lui dit de ne lister que les fichiers (une fois) au lieu de lister les correspondances dans chaque fichier.
pjz le
8
Pas une bonne solution, dépendre de ce comportement (non documenté, orienté vers la consommation humaine) du fileprogramme. C'est très fragile. Pour (juste un) exemple: cela ne fonctionne pas avec les fichiers XML, les filerapports XML document textquel que soit le type de nouvelle ligne.
leonbloy
1
@leonbloy, l'option semble être une minuscule -m /dev/nullsur mon find (GNU findutils) 4.4.2(Ubuntu 12.04).
EarlCrapstone
8
J'aime bien cette réponse. Je l'ai simplement faitfind . -type f | xargs file | grep CRLF
brianz
58
grep -IUlr $'\r'

expliquehell.com - grep -IUlr

Steven Penny
la source
11
Merci! Pour plus de clarté pour ceux qui viennent après, le manuel de bash dit: "Les mots de la forme $ 'string' sont traités spécialement. Le mot se développe en chaîne, avec des caractères d'échappement par barre oblique inversée remplacés comme spécifié par la norme ANSI C." (voir aussi cette liste de codes pris en charge )
Sean Gugler
5
Alors, est-ce spécifique à cette bash? Il convient de noter si c'est le cas.
cubuspl42
pour git avec mal autocrlf, j'utiliserais: grep -IUlrZ $ '\ r' | xargs -0 sed -zbi 's / \ r // g'
buzard
16

Si votre version de grep prend en charge l' option -P (--perl-regexp) , alors

grep -lUP '\r$'

peut être utilisé.

Linuline
la source
8
# list files containing dos line endings (CRLF)

cr="$(printf "\r")"    # alternative to ctrl-V ctrl-M

grep -Ilsr "${cr}$" . 

grep -Ilsr $'\r$' .   # yet another & even shorter alternative
yabt
la source
3

La requête était une recherche ... J'ai un problème similaire ... quelqu'un a soumis des fins de ligne mixtes dans le contrôle de version, donc maintenant nous avons un tas de fichiers avec 0x0d 0x0d 0x0a fins de ligne. Notez que

grep -P '\x0d\x0a'

trouve toutes les lignes, alors que

grep -P '\x0d\x0d\x0a'

et

grep -P '\x0d\x0d'

ne trouve pas de lignes donc il peut y avoir quelque chose d '"autre" à l'intérieur de grep quand il s'agit de motifs de fin de ligne ... malheureusement pour moi!

Peter Y
la source
3

Vous pouvez utiliser la commande de fichier sous unix. Il vous donne le codage des caractères du fichier ainsi que les terminateurs de ligne.

$ file myfile
myfile: ISO-8859 text, with CRLF line terminators
$ file myfile | grep -ow CRLF
CRLF  
Murali Krishna Parimi
la source
1

Si, comme moi, votre unix minimaliste n'inclut pas de subtilités comme la commande file , et que les barres obliques inverses dans vos expressions grep ne coopèrent tout simplement pas, essayez ceci:

$ for file in `find . -type f` ; do
> dump $file | cut -c9-50 | egrep -m1 -q ' 0d| 0d'
> if [ $? -eq 0 ] ; then echo $file ; fi
> done

Les modifications que vous voudrez peut-être apporter à ce qui précède incluent:

  • modifiez la commande find pour localiser uniquement les fichiers que vous souhaitez analyser
  • changez la commande de vidage en od ou tout autre utilitaire de vidage de fichier que vous avez
  • confirmez que la commande cut inclut à la fois un espace de début et de fin ainsi que le caractère hexadécimal de sortie de l' utilitaire de vidage
  • limiter la sortie de vidage aux 1000 premiers caractères environ pour plus d'efficacité

Par exemple, quelque chose comme ça peut fonctionner pour vous en utilisant od au lieu de dump :

 od -t x2 -N 1000 $file | cut -c8- | egrep -m1 -q ' 0d| 0d|0d$'
MykennaC
la source
1

dos2unix a une option d'informations sur les fichiers qui peut être utilisée pour afficher les fichiers qui seraient convertis:

dos2unix -ic /path/to/file

Pour faire cela de manière récursive, vous pouvez utiliser bashl' globstaroption de, qui pour le shell actuel est activée avec shopt -s globstar:

dos2unix -ic **      # all files recursively
dos2unix -ic **/file # files called “file” recursively

Vous pouvez également utiliser findpour cela:

find -exec dos2unix -ic {} +            # all files recursively
find -name file -exec dos2unix -ic {} + # files called “file” recursively
dessert
la source