Ersetzen Sie mehrere Leerzeichen durch ein Leerzeichen, indem Sie nur 'tr' verwenden

71

Ich habe eine Akte f1.txt:

ID     Name
1      a
2         b
3   g
6            f

Die Anzahl der Leerzeichen ist nicht festgelegt. Was ist der beste Weg, um alle Leerzeichen mit nur einem Leerzeichen zu ersetzen tr?

Das habe ich bisher:

cat f1.txt | tr -d " "

Aber die Ausgabe ist:

IDName
1a
2b
3g
6f

Aber ich möchte, dass es so aussieht:

ID Name
1 a
2 b
3 g
6 f

Bitte versuchen Sie es zu vermeiden sed.

gkmohit
quelle
6
Warum ist es so wichtig, sed zu vermeiden? Verwenden Sie, was auch immer funktioniert!
David Richerby
7
Weil ich weiß, wie es geht sed.
Wollten

Antworten:

106

Mit trVerwenden Sie die squeeze Wiederholungsoption:

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

Oder Sie können eine awkLösung verwenden:

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

Wenn Sie ein Feld in einem Datensatz ändern, erstellen Sie es awkneu $0, nehmen Sie alle Felder und verknüpfen Sie sie, getrennt durch OFS, wobei es sich standardmäßig um ein Leerzeichen handelt.

Dadurch werden Abfolgen von Leerzeichen und Tabulatoren (und möglicherweise andere Leerzeichen, je nach Gebietsschema und Implementierung von awk) in einem Leerzeichen zusammengefasst, aber auch die führenden und nachfolgenden Leerzeichen in jeder Zeile entfernt.

cuonglm
quelle
1
Dies ist auch eine großartige Lösung. . . Ich weiß nicht, welches ich jetzt wählen soll: / @Gnouc
gkmohit
Sie können sich für eine Lösung entscheiden, die Ihnen gefällt und die für Sie funktioniert. Ein Hinweis, dass meine Lösung mit der Antwort von @polym anders ist.
Cuonglm
1
:)) Yay! Die Antwort von @Gnouc ist sehr dynamisch, weil er awkalles kann, was er braucht . Sie können auch seine Lösung akzeptieren. Nur eine Sache: Gnouc können Sie möglicherweise erklären, was das awk-Format in Ihrem Befehl bewirkt? Können Sie auch Tabulatoren / Leerzeichen hinzufügen, damit die Ausgabe der erwarteten Ausgabe von Unknown entspricht?
Polym
1
@polym: Mit Unknowns letzter Bearbeitung scheint er nur ein Leerzeichen zu wollen, nicht wie column -tes ausgegeben wird. Erklärung für hinzufügen awk.
Dienstag,
4
Hier gibt es einen kleinen Unterschied. trersetzt zwei Leerzeichen am Ende einer Zeile durch ein einzelnes Leerzeichen. awkentfernt alle nachgestellten Leerzeichen.
Anne van Rossum
19

Verwenden Sie einfach column:

column -t inputFile

Ausgabe:

ID  Name
1   a
2   b
3   g
6   f
Polym
quelle
Wunderbar und eine schnelle Antwort :)
Gkmohit
1
@Unbekannt Großartig, im Dienst zu sein :)!
Polym
1
@ Gnouc wow cool, Spalte nimmt auch eine Datei als Argument. nett, danke!
Polym
Wie kann ich die zweite Spalte nur bekommen, wenn ich will? Ich habe es versucht, column -t f1.txt | cut -d " " -f2 aber es war keine Lösung, die ich erwartet hatte
gkmohit
2
Dann benutze awk: column -t file | awk '{print $2}'druckt nur die zweite Spalte
polym 22.07.14
8

Wenn Sie "Leerzeichen" ausdrücken möchten, müssen Sie die vordefinierten Zeichensätze von tr verwenden: ": Leerzeichen:" (horizontale Leertaste und Leerzeichen) oder ": Leerzeichen:" (verisches Leerzeichen):

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

Beispiele wurden unter Red Hat 5 (GNU tr) ausgeführt.

In meinem Fall wollte ich alle Leerzeichen auf ein einzelnes Leerzeichen normieren, damit ich mich auf das Leerzeichen als Delmitter verlassen konnte.

Wie in Dastrobus zweitem Kommentar hervorgehoben, habe ich den Wortlaut in der Manpage verpasst:

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

Dies ermöglicht es uns, den ersten tr zu eliminieren. Kudo muss angesichts meiner Dichte nach seiner Geduld suchen.

Vorher Parsing-Port von Redis Config. Datei:

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

Danach wird SET2 mit squeeze angegeben:

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

Ausgabe:

6379

Weitere Informationen zu den Weißraum-Nuancen

Zeigen Sie, wo Squeeze alleine fehlschlägt, wenn aufeinanderfolgende gemischte Zeichen der Zeichenklasse [: blank:] beteiligt sind:

 /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

Hinweis: Meine zwei Zeichenkettenfelder im printf-Format sind durch 1 Leerzeichen, 1 Tabulator, 1 Leerzeichen getrennt. Nach dem Auspressen ist diese Reihenfolge noch vorhanden. In der Ausgabe des Octal-Dumps wird dies durch die ASCII-Sequenz 040 011 040 dargestellt.

user3183018
quelle
1
Haben Sie wirklich brauchen tr "[:blank:]" " " | tr -s "[:blank:]"? Ich denke, der erste Teil wird ausreichen, dh tr "[:blank:]" " "da er Leerzeichen normalisiert und die Substitution bereits vornimmt. Aus der Manpage: "Drücken Sie die Zeichen mehrmals zusammen, [...] nachdem alle Lösch- und Übersetzungsvorgänge abgeschlossen sind."
Dastrobu
2
also ´tr -s "[: blank:]" ""´ sollte es tun, übersetzt zuerst alle Leerzeichen in Leerzeichen und drückt dann die Leerzeichen zusammen. Keine Notwendigkeit für eine Sekunde ´Tr´.
Dastrobu
1
Ich habe es versucht printf 'ID \t Name\n' | tr -s "[:blank:]" " " | od -cb(wie von @dastrobu vorgeschlagen) und ich habe ID Name\n(mit einem Leerzeichen) als Ausgabe bekommen. Hast du es tatsächlich versucht, @ user3183018?
Scott
1
OK, lassen Sie mich das noch einmal versuchen. Ich tat printf 'ID␣\t␣Name\n' | tr -s "[:blank:]" "␣"  (wie von @dastrobu vorgeschlagen), wo ein Leerzeichen darstellt, und ich bekam ID␣Name\n(mit einem Leerzeichen) als Ausgabe. Dies ist genau das gleiche wie in Ihrem Beispiel für "Port <SPACE> <TAB> <SPACE> 6379", außer dass ich die Überschriften aus der Frage verwendet habe. Ich frage mich, ob Sie es versucht haben  tr -s "[:blank:]"(ohne das letzte "␣"Argument).
Scott
1
Wenn ich das tue printf 'ID \t Name\n' | od -cb, zeigt es genau, was es soll: ID ⁠  \t ⁠  N a m e \n(dh  ID 040 011 040 N a m e\n). In der Zwischenzeit machen Sie nach eigenen Angaben genau den Fehler, den ich vermutet habe: Sie führen aus tr -s "[:blank:]"(dh  trmit einer Option und  einem Argument), anstelle des Befehls, den @dastrobu und ich jetzt viermal angegeben haben: tr -s '[:blank:]' '␣'(dh  trmit einer Option und  zwei Argumenten ).
Scott
5

Wer braucht ein Programm (außer der Shell)?

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

Wenn Sie möchten, dass die Werte in der zweiten Spalte wie in der columnAntwort von polym angezeigt werden , verwenden Sie printfanstelle von echo:

while read a b
do
    printf '%-2s %s\n' "$a" "$b"
done < f1.txt
Scott
quelle
1
Erstens, im Vergleich zu tr - dies ist ein schrecklich schwacher Vorschlag, was die Effizienz betrifft, es sei denn, die Eingabe ist zu klein, um die winzigen Kosten für trden Aufruf zu überwiegen - was nicht zu erwähnen ist, wie viel Arbeit das Schreiben erfordert . Zuletzt, würden Sie nicht sagen, dass dieser Beitrag die gestellte Frage nicht wirklich beantwortet? Was ist der beste Weg, um alle Leerzeichen mit nur tr durch ein Leerzeichen zu ersetzen?
MikeServ
1
Und außerdem - könnten Sie nicht einfach etwas damit anfangen $IFS? Vielleicht mag: IFS=' <tab>' set -f ; echo $(cat <file)?
mikeserv
2

Dies ist eine alte Frage und oft gelöst. Der Vollständigkeit halber: Ich hatte ein ähnliches Problem, wollte aber Leitungen per Pipe an ein anderes Programm übergeben. Ich habe Xargs benutzt .

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

also cat f1.txt | xargs -L1scheint genau das auszugeben was du willst.

Martin Seitl
quelle