Comment puis-je joindre plusieurs lignes en une seule ligne, avec un séparateur contenant les caractères de nouvelle ligne, en évitant les séparateurs de fin et, éventuellement, les lignes vides?
Exemple. Considérons un fichier texte, foo.txt
, avec trois lignes:
foo
bar
baz
La sortie souhaitée est:
foo,bar,baz
La commande que j'utilise maintenant:
tr '\n' ',' <foo.txt |sed 's/,$//g'
Idéalement, ce serait quelque chose comme ceci:
cat foo.txt |join ,
C'est quoi:
Bien sûr, je pourrais écrire quelque chose ou simplement utiliser un alias. Mais je suis intéressé de connaître les options.
Peut-être un peu surprenant, paste
est un bon moyen de faire ceci:
paste -s -d","
Cela ne traitera pas les lignes vides que vous avez mentionnées. Pour cela, dirigez votre texte dans grep
, tout d'abord:
grep -v '^$' | paste -s -d"," -
Cette sed
ligne devrait fonctionner -
sed -e :a -e 'N;s/\n/,/;ba' file
Test:
[jaypal:~/Temp] cat file
foo
bar
baz
[jaypal:~/Temp] sed -e :a -e 'N;s/\n/,/;ba' file
foo,bar,baz
Pour gérer les lignes vides, vous pouvez supprimer les lignes vides et les diriger vers la ligne ci-dessus.
sed -e '/^$/d' file | sed -e :a -e 'N;s/\n/,/;ba'
Que diriez-vous d'utiliser xargs?
pour votre cas
$ cat foo.txt | sed 's/$/, /' | xargs
Faites attention à la longueur limite de la commande xargs. (Cela signifie qu'un fichier d'entrée très long ne peut pas être traité par ceci.)
Perl:
cat data.txt | Perl -pe 'if(!eof){chomp;$_.=","}'
ou encore plus court et plus rapide, étonnamment:
cat data.txt | Perl -pe 'if(!eof){s/\n/,/}'
ou si vous voulez:
cat data.txt | Perl -pe 's/\n/,/ unless eof'
Juste pour le plaisir, voici une solution entièrement intégrée
IFS=$'\n' read -r -d '' -a data < foo.txt ; ( IFS=, ; echo "${data[*]}" ; )
Vous pouvez utiliser printf
au lieu de echo
si le retour à la ligne final pose problème.
Cela fonctionne en définissant IFS
, les délimiteurs sur lesquels read
sera scindé, uniquement à la nouvelle ligne et non aux autres espaces, puis en indiquant à read
de ne pas arrêter la lecture tant qu'elle n'atteint pas une nul
au lieu de la nouvelle ligne utilisée, et d'ajouter chaque élément lu les données du tableau (-a
). Ensuite, dans un sous-shell afin de ne pas masquer la IFS
du shell interactif, définissons IFS
sur ,
et développons le tableau avec *
, qui délimite chaque élément du tableau avec le premier caractère de IFS
.
Un moyen simple de joindre les lignes avec de l’espace in-situ en utilisant ex
(en ignorant également les lignes vides), utilisez:
ex +%j -cwq foo.txt
Si vous souhaitez imprimer les résultats sur la sortie standard, essayez:
ex +%j +%p -scq! foo.txt
Pour joindre des lignes sans espaces, utilisez +%j!
au lieu de +%j
.
Pour utiliser un délimiteur différent, c'est un peu plus compliqué:
ex +"g/^$/d" +"%s/\n/_/e" +%p -scq! foo.txt
où g/^$/d
(ou v/\S/d
) supprime les lignes vides et s/\n/_/
est une substitution qui fonctionne fondamentalement de la même façon que si vous utilisiez sed
, mais pour toutes les lignes (%
). Une fois l'analyse terminée, imprimez le tampon (%p
). Et enfin, -cq!
exécutant la commande vi q!
, qui se ferme fondamentalement sans enregistrer (-s
consiste à mettre la sortie en mode silence).
Veuillez noter que ex
est équivalent à vi -e
.
Cette méthode est assez portable car la plupart des systèmes Linux/Unix sont livrés avec ex
/vi
par défaut. Et il est plus compatible que d'utiliser sed
où le paramètre sur place (-i
) n'est pas une extension standard et que l'utilitaire it-self est plus orienté flux, ce qui le rend moins portable.
J'ai eu un fichier journal où certaines données ont été divisées en plusieurs lignes. Lorsque cela s'est produit, le dernier caractère de la première ligne était le point-virgule (;). J'ai rejoint ces lignes en utilisant les commandes suivantes:
for LINE in 'cat $FILE | tr -s " " "|"'
do
if [ $(echo $LINE | egrep ";$") ]
then
echo "$LINE\c" | tr -s "|" " " >> $MYFILE
else
echo "$LINE" | tr -s "|" " " >> $MYFILE
fi
done
Le résultat est un fichier dans lequel les lignes divisées dans le fichier journal étaient une ligne dans mon nouveau fichier.
J'avais besoin de faire quelque chose de similaire, en imprimant une liste de champs séparés par des virgules à partir d'un fichier, et j'étais satisfait de la canalisation de STDOUT vers xargs
et Ruby
, comme ceci:
cat data.txt | cut -f 16 -d ' ' | grep -o "\d\+" | xargs Ruby -e "puts ARGV.join(', ')"