web-dev-qa-db-fra.com

"Rejoindre" concis et portable sur la ligne de commande Unix

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:

  1. le moyen le plus portable, concis et lisible.
  2. la manière la plus concise en utilisant des outils Unix non standard.

Bien sûr, je pourrais écrire quelque chose ou simplement utiliser un alias. Mais je suis intéressé de connaître les options.

69
butt

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"," -
112
Michael J. Barber

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'
12
jaypal singh

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.)

8
plhn

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'
6
mykhal

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.

4
Sorpigal

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

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.

0
kenorb

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.

0
Mark Dyer

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(', ')"
0
mchail