Comment convertir la nouvelle ligne DOS / Windows (CRLF) en nouvelle ligne Unix (LF) dans un script Bash?

336

Comment puis-je par programme (c'est-à-dire ne pas utiliser vi ) convertir des sauts de ligne DOS / Windows en Unix?

Les commandes dos2unixet unix2dosne sont pas disponibles sur certains systèmes. Comment puis-je les émuler avec des commandes comme sed/ awk/ tr?

Coran Molovik
la source
9
En général, installez simplement à l' dos2unixaide de votre gestionnaire de paquets, c'est vraiment beaucoup plus simple et existe sur la plupart des plates-formes.
Brad Koch du
1
D'accord! @BradKoch Simple as 'brew install dos2unix' sur Mac OSX
SmileIT

Réponses:

323

Vous pouvez utiliser trpour convertir de DOS en Unix; toutefois, vous ne pouvez le faire en toute sécurité que si CR n'apparaît dans votre fichier que comme premier octet d'une paire d'octets CRLF. C'est généralement le cas. Vous utilisez ensuite:

tr -d '\015' <DOS-file >UNIX-file

Notez que le nom DOS-fileest différent du nom UNIX-file; si vous essayez d'utiliser le même nom deux fois, vous vous retrouverez sans données dans le fichier.

Vous ne pouvez pas faire l'inverse (avec le «tr» standard).

Si vous savez comment entrer le retour chariot dans un script ( control-V, control-Mpour entrer control-M), alors:

sed 's/^M$//'     # DOS to Unix
sed 's/$/^M/'     # Unix to DOS

où le «^ M» est le caractère de contrôle-M. Vous pouvez également utiliser le mécanisme de bash cotation ANSI-C pour spécifier le retour chariot:

sed $'s/\r$//'     # DOS to Unix
sed $'s/$/\r/'     # Unix to DOS

Cependant, si vous devez le faire très souvent (plus d'une fois, en gros), il est beaucoup plus judicieux d'installer les programmes de conversion (par exemple dos2unixet unix2dos, ou peut dtou- être et utod) et de les utiliser.

Si vous devez traiter des répertoires et sous-répertoires entiers, vous pouvez utiliser zip:

zip -r -ll zipfile.zip somedir/
unzip zipfile.zip

Cela va créer une archive zip avec des fins de ligne changées de CRLF en CR. unzipmettra ensuite les fichiers convertis en place (et vous demandera fichier par fichier - vous pouvez répondre: Oui à tous). Remerciements à @vmsnomad pour l'avoir signalé.

Jonathan Leffler
la source
9
en utilisant tr -d '\015' <DOS-file >UNIX-fileDOS-file== UNIX-filerésulte juste en un fichier vide. Malheureusement, le fichier de sortie doit être un fichier différent.
Buttle Butkus
3
@ButtleButkus: Eh bien, oui; c'est pourquoi j'ai utilisé deux noms différents. Si vous zappez le fichier d'entrée avant que le programme ne lise tout, comme vous le faites lorsque vous utilisez deux fois le même nom, vous vous retrouvez avec un fichier vide. C'est un comportement uniforme sur les systèmes de type Unix. Il nécessite un code spécial pour gérer l'écrasement d'un fichier d'entrée en toute sécurité. Suivez les instructions et tout ira bien.
Jonathan Leffler
Il semble que je me souvienne de la fonctionnalité de recherche-remplacement dans le fichier.
Buttle Butkus
4
Il y a des endroits; vous devez savoir où les trouver. Dans certaines limites, l' sedoption GNU -i(pour sur place) fonctionne; les limites sont les fichiers liés et les liens symboliques. La sortcommande a «toujours» (depuis 1979, sinon plus tôt) pris en charge l' -ooption qui peut répertorier l'un des fichiers d'entrée. Cependant, c'est en partie parce que sortdoit lire toutes ses entrées avant de pouvoir écrire l'une de ses sorties. D'autres programmes prennent sporadiquement en charge l'écrasement d'un de leurs fichiers d'entrée. Vous pouvez trouver un programme à usage général (script) pour éviter les problèmes dans «l'environnement de programmation UNIX» de Kernighan & Pike.
Jonathan Leffler
3
La troisième option a fonctionné pour moi, merci. J'ai utilisé l'option -i: sed -i $'s/\r$//' filename- pour éditer sur place. Je travaille sur une machine qui n'a pas accès à Internet, donc l'installation de logiciels pose problème.
Warren Dew
64
tr -d "\r" < file

jetez un oeil ici pour des exemples en utilisant sed:

# IN UNIX ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format.
sed 's/.$//'               # assumes that all lines end with CR/LF
sed 's/^M$//'              # in bash/tcsh, press Ctrl-V then Ctrl-M
sed 's/\x0D$//'            # works on ssed, gsed 3.02.80 or higher

# IN UNIX ENVIRONMENT: convert Unix newlines (LF) to DOS format.
sed "s/$/`echo -e \\\r`/"            # command line under ksh
sed 's/$'"/`echo \\\r`/"             # command line under bash
sed "s/$/`echo \\\r`/"               # command line under zsh
sed 's/$/\r/'                        # gsed 3.02.80 or higher

Utilisez sed -ipour la conversion sur place, par exemple sed -i 's/..../' file.

ghostdog74
la source
10
J'ai utilisé une variante car mon fichier n'avait que \r:tr "\r" "\n" < infile > outfile
Matt Todd
1
@MattTodd pourriez-vous poster ceci comme réponse? le -dest présenté plus fréquemment et n'aidera pas dans la "seule \r" situation.
n611x007
5
Notez que le mappage proposé a \rpour \neffet de double-espacer les fichiers; chaque ligne CRLF unique se terminant par DOS devient \n\nsous Unix.
Jonathan Leffler
Puis-je le faire récursivement?
Aaron Franke
36

Faire cela avec POSIX est délicat:

  • POSIX Sed ne prend pas en charge \rou \15. Même si c'est le cas, l'option en place -in'est pas POSIX

  • POSIX Awk ne prend pas en charge \ret \15, cependant, l' -i inplaceoption n'est pas POSIX

  • d2u et dos2unix ne sont pas des utilitaires POSIX , mais ex est

  • Ex Posix ne supporte pas \r, \15, \nou\12

Pour supprimer les retours chariot:

ex -bsc '%!awk "{sub(/\r/,\"\")}1"' -cx file

Pour ajouter des retours chariot:

ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx file
Steven Penny
la source
2
Il ressemble aux supports POSIX . tr\r Vous pouvez donc également utiliser printf '%s\n' '%!tr -d "\r"' x | ex file(bien que cela soit accordé, cela a été supprimé \rmême s'il n'est pas immédiatement précédent \n). De plus, l' -boption à exn'est pas spécifiée par POSIX.
Wildcard
1
Faire cela dans POSIX est facile. Intégrez le littéral CR dans le script en le tapant (c'est control-M).
Joshua
28

Vous pouvez utiliser vim par programme avec l'option -c {commande}:

Dos à Unix:

vim file.txt -c "set ff=unix" -c ":wq"

Unix à dos:

vim file.txt -c "set ff=dos" -c ":wq"

"set ff = unix / dos" signifie changer le format de fichier (ff) du fichier au format de fin de ligne Unix / DOS

": wq" signifie écrire un fichier sur le disque et quitter l'éditeur (permettant d'utiliser la commande en boucle)

Johan Zicola
la source
3
Cela semblait être la solution la plus élégante mais le manque d'explication sur ce que signifie wq est regrettable.
Jorrick Sleijster
5
Quiconque utilise visaura ce que cela :wqsignifie. Pour ceux qui ne veulent pas que les 3 caractères signifient 1) ouvrir la zone de commande vi, 2) écrire et 3) quitter.
David Newcomb
Je ne savais pas que vous pouviez ajouter de manière interactive des commandes à vim à partir de la CLI
Robert Dundon
vous pouvez utiliser ": x" au lieu de ": wq"
JosephConrad
25

En utilisant AWK, vous pouvez faire:

awk '{ sub("\r$", ""); print }' dos.txt > unix.txt

En utilisant Perl, vous pouvez faire:

perl -pe 's/\r$//' < dos.txt > unix.txt
codaddict
la source
2
Une belle solution portable awk .
mklement0
24

Pour convertir un fichier sur place, utilisez

dos2unix <filename>

Pour sortir du texte converti dans un autre fichier, utilisez

dos2unix -n <input-file> <output-file>

Vous pouvez l'installer sur Ubuntu ou Debian avec

sudo apt install dos2unix

ou sur macOS en utilisant homebrew

brew install dos2unix
Boris
la source
1
Je sais que la question demande des alternatives à dos2unix mais c'est le premier résultat google.
Boris
18

Ce problème peut être résolu avec des outils standard, mais il y a suffisamment de pièges pour les imprudents que je vous recommande d'installer la flipcommande, qui a été écrite il y a plus de 20 ans par Rahul Dhesi, l'auteur de zoo. Il fait un excellent travail de conversion des formats de fichiers tout en évitant, par exemple, la destruction par inadvertance de fichiers binaires, ce qui est un peu trop facile si vous vous contentez de modifier chaque CRLF que vous voyez ...

Norman Ramsey
la source
Est-il possible de le faire en streaming, sans modifier le fichier d'origine?
août
@augurar vous pouvez vérifier les "packages similaires" packages.debian.org/wheezy/flip
n611x007
J'ai eu l'expérience de casser la moitié de mon système d'exploitation simplement en exécutant texxto avec un mauvais indicateur. Soyez prudent surtout si vous voulez le faire sur des dossiers entiers.
A_P
14

Les solutions publiées jusqu'à présent ne traitent qu'une partie du problème, convertissant le CRLF de DOS / Windows en LF d'Unix; la partie qui leur manque est que DOS utilise CRLF comme séparateur de ligne , tandis qu'Unix utilise LF comme terminateur de ligne . La différence est qu'un fichier DOS (généralement) n'aura rien après la dernière ligne du fichier, contrairement à Unix. Pour effectuer la conversion correctement, vous devez ajouter ce LF final (sauf si le fichier est de longueur nulle, c'est-à-dire qu'il ne contient aucune ligne). Mon incantation préférée pour cela (avec un peu de logique supplémentaire pour gérer les fichiers séparés par CR de style Mac, et non les fichiers molesters qui sont déjà au format Unix) est un peu de perl:

perl -pe 'if ( s/\r\n?/\n/g ) { $f=1 }; if ( $f || ! $m ) { s/([^\n])\z/$1\n/ }; $m=1' PCfile.txt

Notez que cela envoie la version Unixified du fichier à stdout. Si vous souhaitez remplacer le fichier par une version Unixified, ajoutez l' -iindicateur perl .

Gordon Davisson
la source
@LudovicZenohateLagouardette S'agissait-il d'un fichier texte brut (c.-à-d. Csv ou texte tabulé), ou autre chose? S'il était dans un format de base de données, le manipuler comme s'il s'agissait de texte est très susceptible de corrompre sa structure interne.
Gordon Davisson
Un texte brut csv, mais je pense que la rencontre était étrange. Je pense que ça a gâché à cause de ça. Mais ne vous inquiétez pas. Je collecte toujours des sauvegardes et ce n'était même pas le vrai ensemble de données, juste un 1 Go. Le vrai est un 26 Go.
Ludovic Zenohate Lagouardette du
14

Si vous n'avez pas accès à dos2unix , mais pouvez lire cette page, vous pouvez copier / coller dos2unix.py à partir d'ici.

#!/usr/bin/env python
"""\
convert dos linefeeds (crlf) to unix (lf)
usage: dos2unix.py <input> <output>
"""
import sys

if len(sys.argv[1:]) != 2:
  sys.exit(__doc__)

content = ''
outsize = 0
with open(sys.argv[1], 'rb') as infile:
  content = infile.read()
with open(sys.argv[2], 'wb') as output:
  for line in content.splitlines():
    outsize += len(line) + 1
    output.write(line + '\n')

print("Done. Saved %s bytes." % (len(content)-outsize))

Post-cross du superutilisateur .

anatoly techtonik
la source
1
L'utilisation est trompeuse. Le réel dos2unixconvertit tous les fichiers d'entrée par défaut. Votre utilisation implique un -nparamètre. Et le réel dos2unixest un filtre qui lit depuis stdin, écrit dans stdout si les fichiers ne sont pas fournis.
jfs
8

Super duper facile avec PCRE;

En tant que script, ou remplacez $@par vos fichiers.

#!/usr/bin/env bash
perl -pi -e 's/\r\n/\n/g' -- $@

Cela écrasera vos fichiers en place!

Je recommande de ne le faire qu'avec une sauvegarde (contrôle de version ou autre)

ThorSummoner
la source
Je vous remercie! Cela fonctionne, même si j'écris le nom de fichier et non-- . J'ai choisi cette solution car elle est facile à comprendre et à adapter pour moi. Pour info, voici ce que font les commutateurs: -psupposer une boucle "while input", -iéditer le fichier d'entrée en place, -eexécuter la commande suivante
Rolf
À proprement parler, PCRE est une réimplémentation du moteur regex de Perl, pas le moteur regex de Perl. Ils ont tous les deux cette capacité, bien qu'il y ait aussi des différences, malgré l'implication dans le nom.
tripleee
6

Une solution awk encore plus simple sans programme:

awk -v ORS='\r\n' '1' unix.txt > dos.txt

Techniquement, «1» est votre programme, b / c awk en nécessite un lorsque l'option est donnée.

MISE À JOUR : Après avoir revu cette page pour la première fois depuis longtemps, je me suis rendu compte que personne n'avait encore posté de solution interne, alors en voici une:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt
nawK
la source
C'est pratique, mais juste pour être clair: cela traduit Unix -> Windows / DOS, qui est la direction opposée à ce que l'OP a demandé.
mklement0
5
Cela a été fait exprès, laissé comme exercice à l'auteur. eyerolls awk -v RS='\r\n' '1' dos.txt > unix.txt
nawK
Super (et bravo à vous pour la finesse pédagogique).
mklement0
1
"b / c awk en requiert un lorsque l'option est donnée." - awk nécessite toujours un programme, que les options soient spécifiées ou non.
mklement0
1
La solution bash pure est intéressante, mais beaucoup plus lente qu'un équivalent awkou une sedsolution. En outre, vous devez utiliser while IFS= read -r linepour conserver fidèlement les lignes d'entrée, sinon les espaces de début et de fin sont coupés (en variante, n'utilisez aucun nom de variable dans la readcommande et travaillez avec $REPLY).
mklement0
5

Je venais de réfléchir à cette même question (côté Windows, mais également applicable à Linux.) Étonnamment, personne n'a mentionné une manière très automatisée de faire la conversion CRLF <-> LF pour les fichiers texte en utilisant un bon vieux zip -ll option (Info-ZIP):

zip -ll textfiles-lf.zip files-with-crlf-eol.*
unzip textfiles-lf.zip 

REMARQUE: cela créerait un fichier zip préservant les noms de fichiers d'origine mais convertissant les fins de ligne en LF. Ensuite unzip, les fichiers seraient extraits au format zip, c'est-à-dire avec leurs noms d'origine (mais avec les terminaisons LF), ce qui inviterait à remplacer les fichiers d'origine locaux, le cas échéant.

Extrait pertinent de zip --help:

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)
vmsnomad
la source
Meilleure réponse, selon moi, car elle peut traiter des répertoires et sous-répertoires entiers. Je suis content d'avoir creusé aussi loin.
caram
5

fait intéressant dans mon git-bash sur windows sed ""a déjà fait l'affaire:

$ echo -e "abc\r" >tst.txt
$ file tst.txt
tst.txt: ASCII text, with CRLF line terminators
$ sed -i "" tst.txt
$ file tst.txt
tst.txt: ASCII text

Ma conjecture est que sed les ignore lors de la lecture des lignes de l'entrée et écrit toujours les fins de ligne Unix sur la sortie.

user829755
la source
4

Cela a fonctionné pour moi

tr "\r" "\n" < sampledata.csv > sampledata2.csv 
Santosh
la source
9
Cela va convertir chaque seule DOS nouvelle ligne en deux UNIX-retour à la ligne.
Melebius
2

Pour Mac osx si vous avez installé homebrew [ http://brew.sh/

brew install dos2unix

for csv in *.csv; do dos2unix -c mac ${csv}; done;

Assurez-vous d'avoir fait des copies des fichiers, car cette commande modifiera les fichiers en place. L'option -c mac rend le commutateur compatible avec osx.

Ashley Raiteri
la source
Cette réponse ne correspond vraiment pas à la question de l'affiche originale.
hlin117
2
Les utilisateurs d'OS X ne devraient pas utiliser -c mac, ce qui est pour convertir les CRnouvelles lignes pré-OS X uniquement. Vous souhaitez utiliser ce mode uniquement pour les fichiers vers et depuis Mac OS 9 ou antérieur.
askewchan
2

TIMTOWTDI!

perl -pe 's/\r\n/\n/; s/([^\n])\z/$1\n/ if eof' PCfile.txt

Basé sur @GordonDavisson

Il faut considérer la possibilité de [noeol]...

lzc
la source
2

Vous pouvez utiliser awk. Définissez le séparateur d'enregistrement ( RS) sur une expression rationnelle qui correspond à tous les caractères de nouvelle ligne ou aux caractères possibles. Et définissez le séparateur d'enregistrement de sortie ( ORS) sur le caractère de nouvelle ligne de style Unix.

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt
kazmer
la source
C'est celui qui a fonctionné pour moi (MacOS, git diffmontre ^ M, édité dans vim)
Dorian
2

Sous Linux, il est facile de convertir ^ M (ctrl-M) en * nix newlines (^ J) avec sed.

Ce sera quelque chose comme ça sur la CLI, il y aura en fait un saut de ligne dans le texte. Cependant, le \ passe ce ^ J à sed:

sed 's/^M/\
/g' < ffmpeg.log > new.log

Vous obtenez cela en utilisant ^ V (ctrl-V), ^ M (ctrl-M) et \ (barre oblique inverse) pendant que vous tapez:

sed 's/^V^M/\^V^J/g' < ffmpeg.log > new.log
jet
la source
2
sed --expression='s/\r\n/\n/g'

Étant donné que la question mentionne sed, c'est la façon la plus simple d'utiliser sed pour y parvenir. Ce que dit l'expression, c'est remplacer tous les retours chariot et sauts de ligne par seulement des sauts de ligne uniquement. C'est ce dont vous avez besoin lorsque vous passez de Windows à Unix. J'ai vérifié que cela fonctionne.

John Paul
la source
Hey John Paul - cette réponse a été signalée pour suppression, donc est apparue dans une file d'attente pour moi. En général, lorsque vous avez une question comme celle-ci qui a 8 ans, avec 22 réponses, vous voudrez expliquer comment votre réponse est utile d'une manière que les autres réponses existantes ne le sont pas.
zzxyz
0

En tant qu'extension de la solution Unix vers DOS de Jonathan Leffler, pour convertir en toute sécurité en DOS lorsque vous n'êtes pas sûr des fins de ligne actuelles du fichier:

sed '/^M$/! s/$/^M/'

Cela vérifie que la ligne ne se termine pas déjà dans CRLF avant la conversion en CRLF.

Fou de Bassan
la source
0

J'ai créé un script basé sur la réponse acceptée afin que vous puissiez le convertir directement sans avoir besoin d'un fichier supplémentaire à la fin et le supprimer et le renommer ensuite.

convert-crlf-to-lf() {
    file="$1"
    tr -d '\015' <"$file" >"$file"2
    rm -rf "$file"
    mv "$file"2 "$file"
}

assurez-vous simplement que si vous avez un fichier comme "file1.txt" que "file1.txt2" n'existe pas déjà ou qu'il sera écrasé, je l'utilise comme un emplacement temporaire pour stocker le fichier.

OZZIE
la source
0

Avec bash 4.2 et plus récent, vous pouvez utiliser quelque chose comme ceci pour supprimer le CR de fin, qui utilise uniquement les fonctions intégrées bash:

if [[ "${str: -1}" == $'\r' ]]; then
    str="${str:: -1}"
fi
glevand
la source
-3

J'ai essayé le fichier.txt de sed / ^ M $ // 'sur OSX ainsi que plusieurs autres méthodes ( http://www.thingy-ma-jig.co.uk/blog/25-11-2010/fixing- dos-line-endings ou http://hintsforums.macworld.com/archive/index.php/t-125.html ). Aucun n'a fonctionné, le fichier est resté inchangé (entre Ctrl-v Enter était nécessaire pour reproduire ^ M). Au final, j'ai utilisé TextWrangler. Ce n'est pas strictement une ligne de commande mais cela fonctionne et il ne se plaint pas.

mercergeoinfo
la source