Remplacez plusieurs espaces par un seul en utilisant 'tr' seulement

72

J'ai un fichier, f1.txt:

ID     Name
1      a
2         b
3   g
6            f

Le nombre d'espaces n'est pas fixe. Quelle est la meilleure façon de remplacer tous les espaces blancs par un seul espace tr?

Voici ce que j'ai jusqu'à présent:

cat f1.txt | tr -d " "

Mais le résultat est:

IDName
1a
2b
3g
6f

Mais je veux que ça ressemble à ça:

ID Name
1 a
2 b
3 g
6 f

S'il vous plaît essayez d'éviter sed.

gkmohit
la source
6
Pourquoi est-il si important d'éviter sed? Utilisez ce qui fonctionne!
David Richerby
7
Parce que je sais comment faire avec sed. Voulait savoir d'autres façons
:)

Réponses:

107

Avec tr, utilisez l' option squeeze repeat:

$ tr -s " " < file
ID Name
1 a
2 b
3 g
6 f

Ou vous pouvez utiliser une awksolution:

$ awk '{$2=$2};1' file
ID Name
1 a
2 b
3 g
6 f

Lorsque vous modifiez un champ dans l'enregistrement, awkreconstruisez $0, prend tous les champs et les concaténer, séparés par OFSun espace qui est un espace par défaut.

Cela pressera des séquences d’espace et des tabulations (et éventuellement d’autres caractères blancs en fonction de la localisation et de l’implémentation de awk) dans un seul espace, mais supprimera également les espaces en début et en fin de chaque ligne.

cuonglm
la source
1
C'est une excellente solution aussi. . . Je ne sais pas lequel choisir maintenant: / @Gnouc
gkmohit
N'hésitez pas à choisir la solution que vous aimez et qui fonctionne pour vous. Une note que ma solution est différente avec la réponse de @ polym.
jeudi
1
:)) Yay! La réponse de @Gnouc est vraiment dynamique, car il utilise awk, il peut tout faire. Vous pouvez également accepter sa solution. Juste une chose: Gnouc pouvez-vous éventuellement expliquer ce que fait le format awk dans votre commande? De plus, pouvez-vous ajouter des tabulations / espaces afin que la sortie soit conforme à la sortie attendue de Unknown?
polym
1
@polym: Avec la dernière édition de Unknown, il semble ne vouloir qu'un seul espace, pas comme la sortie column -t. Ajouter une explication pour awk.
jeudi
4
Il y a une petite différence ici. trremplacera deux espaces à la fin d'une ligne par un seul espace. awkva supprimer tous les espaces de fin.
Anne van Rossum
19

Il suffit d'utiliser column:

column -t inputFile

Sortie:

ID  Name
1   a
2   b
3   g
6   f
polym
la source
Merveilleux et une réponse rapide :)
gkmohit
1
@Inconnu Super d'être au service :)!
polym
1
@Gnouc wow cool, la colonne prend également un fichier en argument. merci beaucoup!
polym
Comment puis-je obtenir la deuxième colonne uniquement si je le souhaite? J'ai essayé, column -t f1.txt | cut -d " " -f2 mais ce n'était pas une solution à laquelle je m'attendais
gkmohit
2
Utilisez awk then: column -t file | awk '{print $2}'
affiche
8

Si vous voulez utiliser "l'espace blanc", vous voudrez utiliser les jeux de caractères prédéfinis de tr ": blank:" (onglet et espace d'espacement horizontal) ou ": space:" (espaces blancs réels):

/bin/echo -e  "val1\t\tval2   val3" | tr -s "[:blank:]"

Des exemples ont été exécutés sur Red Hat 5 (GNU tr).

Dans mon cas, je voulais normaliser tous les espaces blancs en un seul espace afin de pouvoir compter sur cet espace en tant que delmitter.

Comme l'a souligné le deuxième commentaire de dastrobu, j'ai raté le libellé de la page de manuel:

 -s uses the last specified SET, and occurs after translation or deletion.

Cela nous permet d’éliminer le premier tr. Kudo est à la recherche de ses patients face à ma densité.

Avant, analyse du port de Redis config. fichier:

grep "^port" $redisconf | tr "[:blank:]" " " | tr -s "[:blank:]"  | cut -d" " -f2

Après, avec SET2 étant spécifié avec le squeeze:

grep "^port" $redisconf | tr -s "[:blank:]" " " | cut -d" " -f2

Sortie:

6379

Pour plus de détails sur les nuances des espaces

Démontrez les cas où squeeze seul échoue lorsque des caractères mélangés successifs appartenant à la classe de caractères [: blank:] sont impliqués:

 /usr/bin/printf '%s \t %s' id myname | tr -s "[:blank:]"  | od -cb
0000000   i   d      \t       m   y   n   a   m   e
        151 144 040 011 040 155 171 156 141 155 145
0000013

Remarque: Mes deux champs de chaîne au format printf sont séparés par 1 espace, 1 onglet, 1 espace. Après la compression, cette séquence existe toujours. Dans la sortie du vidage Octal, cela est représenté par la séquence ascii 040 011 040.

utilisateur3183018
la source
1
Avez-vous vraiment besoin tr "[:blank:]" " " | tr -s "[:blank:]"? Je suppose que la première partie suffira, c'est-à-dire tr "[:blank:]" " "qu'elle normalise les espaces et effectue déjà la substitution. Extrait de la page de manuel: "Réduisez plusieurs occurrences des caractères [...] après la suppression et la traduction terminées."
Dastrobu
2
alors ´tr -s "[: blank:]" "" ´ devrait le faire, il convertit d’abord tous les espaces en espaces, puis réduit les espaces. Pas besoin d'un deuxième ´tr´.
dastrobu
1
J'ai essayé printf 'ID \t Name\n' | tr -s "[:blank:]" " " | od -cb(comme suggéré par @dastrobu) et j'ai obtenu ID Name\n(avec un espace) en sortie. L'avez-vous réellement essayé, @ user3183018?
Scott
1
OK, laissez-moi essayer de le répéter. Je l'ai fait printf 'ID␣\t␣Name\n' | tr -s "[:blank:]" "␣"  (comme suggéré par @ Dastrobu), où représente un espace, et j'ai ID␣Name\n(avec un espace) en sortie. Ceci est exactement le même que votre exemple de «Port <SPACE> <TAB> <SPACE> 6379», sauf que j'ai utilisé les chaînes de titre de la question. Je me demande si vous avez essayé  tr -s "[:blank:]"(sans l' "␣"argument final ).
Scott
1
Quand je le fais printf 'ID \t Name\n' | od -cb, cela montre exactement ce que cela est supposé: ID ⁠  \t ⁠  N a m e \n(c.-à  ID 040 011 040 N a m e\n-d.). Pendant ce temps, selon votre propre témoignage, vous faites exactement l'erreur que j'ai devinée: vous exécutez tr -s "[:blank:]"(c'est-à-dire  travec une option et  un argument), au lieu de la commande que @dastrobu et moi avons présentée à quatre reprises maintenant: tr -s '[:blank:]' '␣'(c'est-à-dire  travec une option et  deux arguments ).
Scott
5

Qui a besoin d'un programme (autre que le shell)?

while read a b
do
    echo "$a $b"
done < f1.txt

Si vous souhaitez aligner les valeurs de la deuxième colonne, comme dans la columnréponse de polym , utilisez printfplutôt echo:

while read a b
do
    printf '%-2s %s\n' "$a" "$b"
done < f1.txt
Scott
la source
1
En premier lieu, par rapport à tr - il s’agit là d’une suggestion extrêmement faible en termes d’efficacité, à moins que l’entrée soit trop petite et dépasse le coût minime de trl’invocation de -, sans compter le travail supplémentaire que l’écriture nécessite. Enfin, ne diriez-vous pas que ce message ne répond pas réellement à la question posée? Quel est le meilleur moyen de remplacer tous les espaces blancs par un seul espace en utilisant seulement tr?
mikeserv
1
Et en outre - ne pourriez-vous pas plus facilement faire quelque chose avec $IFS? Peut-être comme: IFS=' <tab>' set -f ; echo $(cat <file)?
mikeserv
2

Ceci est une vieille question et résolu à plusieurs reprises. Juste pour être complet: j'avais un problème similaire, mais je voulais faire passer des lignes par le biais d'un programme de canal à l'autre. J'ai utilisé xargs .

-L max-lines
   Use at most max-lines nonblank input lines per command line.
   Trailing blanks cause an input line to be logically continued 
   on the next input line.  Implies -x.

donc cat f1.txt | xargs -L1semble sortie exactement ce que vous voulez.

Martin Seitl
la source