Transformer une chaîne multiligne en une seule virgule séparée


95

Disons que j'ai la chaîne suivante:

something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)

Comment puis-je transformer cela en simplement

+12.0,+15.5,+9.0,+13.5

en bash?


Revenons un instant en arrière et considérons ce fil comme une accusation flagrante de bash en tant que langage de programmation. Considérez Scala listOfStuff mkString ", "ou Haskellintercalate ", " listOfString
FP Librement

Réponses:


92

Vous pouvez utiliser awket sed:

awk -vORS=, '{ print $2 }' file.txt | sed 's/,$/\n/'

Ou si vous souhaitez utiliser un tuyau:

echo "data" | awk -vORS=, '{ print $2 }' | sed 's/,$/\n/'

Pour le décomposer:

  • awk est excellent pour gérer des données décomposées en champs
  • -vORS=,définit le "séparateur d'enregistrement de sortie" sur ,, ce que vous vouliez
  • { print $2 }dit awkd'imprimer le deuxième champ pour chaque enregistrement (ligne)
  • file.txt est votre nom de fichier
  • sedse débarrasse simplement de la fin ,et le transforme en une nouvelle ligne (si vous ne voulez pas de nouvelle ligne, vous pouvez le faire s/,$//)

1
awk: option -v invalide :(
Marsellus Wallace

6
Ajouter un espace entre -v et ORS =, (pour moi, sur osx)
Graham P Heath

Comment faire la même commande pour séparer les tuyaux? awk -v ORS=| '{ print $1 }' DCMC.rtf | sed 's/,$/\n/'je reçois une erreur
Yogesh

2
étrangement, lorsque j'essaye de faire cela, la sortie est vide.
eternaltyro

1
Je pense que pour la version avec piped, cela devrait être {print $1}sinon je n'obtiens que des virgules en sortie
Przemysław Czechowski

162

Propre et simple:

awk '{print $2}' file.txt | paste -s -d, -

3
C'est la meilleure réponse ici, et évidemment la bonne façon de le faire
forresthopkinsa

Comment citer toutes les valeurs avec des guillemets simples / doubles?
Hussain

1
@Hussaincat thing | awk -F',' '{ print "'\''" $7 "'\' '" }' | paste -s -d ','
starbeamrainbowlabs

Comment l'utiliser ,'comme délimiteur?
Kasun Siyambalapitiya

N'oubliez pas de gérer les sauts de ligne Windows (par exemple en utilisant dos2unix) s'il y a des CRLF dans la chaîne.
Bowi le


10
$ awk -v ORS=, '{print $2}' data.txt | sed 's/,$//'
+12.0,+15.5,+9.0,+13.5

$ cat data.txt | tr -s ' ' | cut -d ' ' -f 2 | tr '\n' ',' | sed 's/,$//'
+12.0,+15.5,+9.0,+13.5

bravo, qu'en est-il si l'entrée à awk se fait via une entrée standard (il suffit de mettre function | awk...votre exemple?
Alex Coplan

10

awk une doublure

$ awk '{printf (NR>1?",":"") $2}' file

+12.0,+15.5,+9.0,+13.5

8

Cela devrait fonctionner aussi

awk '{print $2}' file | sed ':a;{N;s/\n/,/};ba'

8

Cela pourrait fonctionner pour vous:

cut -d' ' -f5 file | paste -d',' -s
+12.0,+15.5,+9.0,+13.5

ou

sed '/^.*\(+[^ ]*\).*/{s//\1/;H};${x;s/\n/,/g;s/.//p};d' file
+12.0,+15.5,+9.0,+13.5

ou

sed 's/\S\+\s\+//;s/\s.*//;H;$!d;x;s/.//;s/\n/,/g' file

Pour chaque ligne du fichier; coupez le premier champ et les espaces suivants, coupez le reste de la ligne suivant le deuxième champ et ajoutez à l'espace d'attente. Supprimez toutes les lignes sauf la dernière où nous basculons vers l'espace d'attente et après avoir supprimé la nouvelle ligne introduite au début, convertissez toutes les nouvelles lignes en ,'s.

NB Pourrait être écrit:

sed 's/\S\+\s\+//;s/\s.*//;1h;1!H;$!d;x;s/\n/,/g' file

4

Vous pouvez utiliser grep:

grep -o "+\S\+" in.txt | tr '\n' ','

qui trouve la chaîne commençant par +, suivie de n'importe quelle chaîne \S\+, puis convertit les caractères de nouvelle ligne en virgules. Cela devrait être assez rapide pour les gros fichiers.


4

Essayez ce code simple:

awk '{printf("%s,",$2)}' File1

3

essaye ça:

sedSelectNumbers='s".* \(+[0-9]*[.][0-9]*\) .*"\1,"'
sedClearLastComma='s"\(.*\),$"\1"'
cat file.txt |sed "$sedSelectNumbers" |tr -d "\n" |sed "$sedClearLastComma"

la bonne chose est la partie facile de la suppression des caractères de nouvelle ligne "\ n"!

EDIT: un autre excellent moyen de joindre des lignes en une seule ligne avec sed est celui-ci: |sed ':a;N;$!ba;s/\n/ /g'obtenu d' ici .


Cet EDIT est génial - +1!
JoeG

2

Une solution écrite en pur Bash:

#!/bin/bash

sometext="something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)"

a=()
while read -r a1 a2 a3; do
    # we can add some code here to check valid values or modify them
    a+=("${a2}")
done <<< "${sometext}"
# between parenthesis to modify IFS for the current statement only
(IFS=',' ; printf '%s: %s\n' "Result" "${a[*]}")

Résultat: + 12,0, + 15,5, + 9,0, + 13,5


2

Je n'ai pas vu cette solution simple avec awk

awk 'b{b=b","}{b=b$2}END{print b}' infile

0

Avec perl:

fg@erwin ~ $ perl -ne 'push @l, (split(/\s+/))[1]; END { print join(",", @l) . "\n" }' <<EOF
something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)
EOF

+12.0,+15.5,+9.0,+13.5

0

Vous pouvez également le faire avec deux appels sed:

$ cat file.txt 
something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)
$ sed 's/^[^:]*: *\([+0-9.]\+\) .*/\1/' file.txt | sed -e :a -e '$!N; s/\n/,/; ta'
+12.0,+15.5,+9.0,+13.5

Le premier appel sed supprime les données inintéressantes, et le second joint toutes les lignes.


0

Vous pouvez également imprimer comme ceci:

Just awk: utiliser printf

bash-3.2$ cat sample.log
something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)

bash-3.2$ awk ' { if($2 != "") { if(NR==1) { printf $2 } else { printf "," $2 } } }' sample.log
+12.0,+15.5,+9.0,+13.5

0

Une autre solution Perl, similaire à awk de Dan Fego:

perl -ane 'print "$F[1],"' file.txt | sed 's/,$/\n/'

-a dit à perl de diviser la ligne d'entrée dans le tableau @F, qui est indexé à partir de 0.


0

Eh bien, la partie la plus difficile est probablement de sélectionner la deuxième "colonne" car je ne connais pas un moyen facile de traiter plusieurs espaces comme un seul. Pour le reste, c'est facile. Utilisez des substitutions bash.

# cat bla.txt
something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)

# cat bla.sh
OLDIFS=$IFS
IFS=$'\n'
for i in $(cat bla.txt); do
  i=$(echo "$i" | awk '{print $2}')
  u="${u:+$u, }$i"
done
IFS=$OLDIFS
echo "$u"

# bash ./bla.sh
+12.0, +15.5, +9.0, +13.5
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.