Wie schneide ich führende und nachfolgende Leerzeichen in jeder Zeile einer Ausgabe ab?

155

Ich möchte alle führenden und nachfolgenden Leerzeichen und Tabulatoren aus jeder Zeile in einer Ausgabe entfernen.

Gibt es ein einfaches Tool, in das trimich meine Ausgabe umleiten kann?

Beispieldatei:

test space at back 
 test space at front
TAB at end  
    TAB at front
sequence of some    space in the middle
some empty lines with differing TABS and spaces:





 test space at both ends 
rubo77
quelle
1
Für jeden, der hier nach einer Lösung zum Entfernen von Zeilenumbrüchen sucht, ist das ein anderes Problem. Per Definition erzeugt eine neue Zeile eine neue Textzeile. Daher kann eine Textzeile keine neue Zeile enthalten. Die Frage, die Sie stellen möchten, lautet, wie Sie eine neue Zeile am Anfang oder Ende einer Zeichenfolge entfernen : stackoverflow.com/questions/369758 , oder wie Sie leere Zeilen oder Zeilen mit Leerzeichen entfernen: serverfault.com/questions/252921
Tony

Antworten:

200
awk '{$1=$1;print}'

oder kürzer:

awk '{$1=$1};1'

Trimmt führende und nachfolgende Leerzeichen oder Tabulatorzeichen 1 und drückt auch Folgen von Tabulatoren und Leerzeichen in ein einzelnes Leerzeichen.

Das funktioniert, weil Sie, wenn Sie einem der Felder etwas zuweisen , awkden gesamten Datensatz (wie von gedruckt print) neu erstellen, indem Sie alle Felder ( $1, ..., $NF) mit OFS(standardmäßig Leerzeichen) verbinden.

1 (und möglicherweise andere Leerzeichen, abhängig vom Gebietsschema und der awkImplementierung)

Stéphane Chazelas
quelle
2
Ein Semikolon im zweiten Beispiel ist überflüssig. Könnte verwenden:awk '{$1=$1}1'
Brian
Interessant ... Kein Semikolon wird von Gawk, Mawk und OS Xs awk unterstützt. (Zumindest für meine Versionen (1.2, 4.1.1 bzw. 20070501)
Brian
1
Das einzige, was ich an diesem Ansatz nicht mag, ist, dass Sie sich wiederholende Leerzeichen in der Zeile verlieren. Zum Beispielecho -e 'foo \t bar' | awk '{$1=$1};1'
benutzerfreundlich
2
echo ' hello ' | xargs
JREAM
44

Der Befehl kann wie folgt zusammengefasst werden, wenn Sie GNU verwenden sed:

$ sed 's/^[ \t]*//;s/[ \t]*$//' < file

Beispiel

Hier ist der obige Befehl in Aktion.

$ echo -e " \t   blahblah  \t  " | sed 's/^[ \t]*//;s/[ \t]*$//'
blahblah

Sie können hexdumpdamit bestätigen, dass der sedBefehl die gewünschten Zeichen korrekt entfernt.

$ echo -e " \t   blahblah  \t  " | sed 's/^[ \t]*//;s/[ \t]*$//' | hexdump -C
00000000  62 6c 61 68 62 6c 61 68  0a                       |blahblah.|
00000009

Zeichenklassen

Sie können auch Zeichenklassennamen verwenden, anstatt die Mengen wie folgt buchstäblich aufzulisten [ \t]:

$ sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//' < file

Beispiel

$ echo -e " \t   blahblah  \t  " | sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//'

Die meisten GNU-Tools, die reguläre Ausdrücke (Regex) verwenden, unterstützen diese Klassen.

 [[:alnum:]]  - [A-Za-z0-9]     Alphanumeric characters
 [[:alpha:]]  - [A-Za-z]        Alphabetic characters
 [[:blank:]]  - [ \x09]         Space or tab characters only
 [[:cntrl:]]  - [\x00-\x19\x7F] Control characters
 [[:digit:]]  - [0-9]           Numeric characters
 [[:graph:]]  - [!-~]           Printable and visible characters
 [[:lower:]]  - [a-z]           Lower-case alphabetic characters
 [[:print:]]  - [ -~]           Printable (non-Control) characters
 [[:punct:]]  - [!-/:-@[-`{-~]  Punctuation characters
 [[:space:]]  - [ \t\v\f]       All whitespace chars
 [[:upper:]]  - [A-Z]           Upper-case alphabetic characters
 [[:xdigit:]] - [0-9a-fA-F]     Hexadecimal digit characters

Die Verwendung dieser anstelle von Literalmengen scheint immer eine Verschwendung von Speicherplatz zu sein. Wenn Sie jedoch befürchten, dass Ihr Code portierbar ist oder mit alternativen Zeichensätzen umgehen muss (denken Sie an Internationalität), sollten Sie wahrscheinlich die Klassennamen verwenden stattdessen.

Verweise

slm
quelle
Beachten Sie, dass dies [[:space:]]nicht [ \t]dem allgemeinen Fall (Unicode usw.) entspricht. [[:space:]]wird wahrscheinlich viel langsamer sein (da Unicode viel mehr Arten von Leerzeichen enthält als nur ' 'und '\t'). Das Gleiche für alle anderen.
Olivier Dulac
sed 's/^[ \t]*//'ist nicht tragbar. In POSIX ist es sogar erforderlich, dass eine Folge von Leerzeichen, Backslash oder tZeichen entfernt wird, und genau das sedtut GNU auch, wenn POSIXLY_CORRECTes sich in der Umgebung befindet.
Stéphane Chazelas
Was ist, wenn ich Zeilenumbrüche abschneiden möchte? '\ n \ n Text \ n \ n'
Eugene Biryukov
Ich mag die sed-Lösung wegen des Fehlens anderer Nebenwirkungen wie bei der awk-Lösung. Die erste Variante funktioniert nicht, als ich sie in Bash auf OSX jsut ausprobiert habe, aber die Character-Class-Version funktioniert:sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//'
Tony
@EugeneBiryukov siehe meinen Kommentar zum ursprünglichen Beitrag
Tony
23

Wie von Stéphane Chazelas in der akzeptierten Antwort vorgeschlagen, können Sie jetzt
ein Skript erstellen /usr/local/bin/trim:

#!/bin/bash
awk '{$1=$1};1'

und gib dieser Datei die ausführbaren Rechte:

chmod +x /usr/local/bin/trim

Jetzt können Sie jeden Ausgang zum trimBeispiel übergeben:

cat file | trim

(für die Kommentare unten: Ich habe das schon mal benutzt: while read i; do echo "$i"; done
das funktioniert auch gut, ist aber weniger performant)

rubo77
quelle
1
Viel Glück, wenn Ihre Datei sehr groß ist und / oder Backslashes enthält.
don_crissti
1
@don_crissti: Könnten Sie etwas mehr kommentieren? Welche Lösung passt besser für große Dateien und wie kann ich meine Lösung ändern, wenn die Datei Backslashes enthält?
Rubo77
3
Sie werden verwenden while read -r lineSchrägstriche zu erhalten und selbst dann ... . In Bezug auf riesige Dateien / Geschwindigkeit haben Sie sich für die schlechteste Lösung entschieden. Ich glaube nicht, dass es irgendetwas Schlimmeres gibt. Lesen Sie die Antworten unter Warum wird eine Shell-Schleife zum Verarbeiten von schlechtem Text verwendet? einschließlich meines Kommentars zur letzten Antwort, bei der ich einen Link zu einem Geschwindigkeits-Benchmark hinzugefügt habe. Die sedAntworten hier sind meiner Meinung nach völlig in Ordnung und weitaus besser als read.
don_crissti
@don_crissti ... und / oder hat Zeilen, die mit einer -Kombination von 1 oder mehreren e-, E- oder n-Zeichen beginnen und darauf folgen und / oder NUL-Zeichen enthalten. Auch eine nicht terminierte Zeile nach der letzten neuen Zeile wird übersprungen.
Stéphane Chazelas
1
Sie können auch einen Alias ​​in / etc / profile hinzufügen (oder Ihren Alias ​​trim = "awk '{\ $ 1 = \ $ 1}; 1'"
Jeff Clayton,
22

xargs ohne Argumente machen das.

Beispiel:

trimmed_string=$(echo "no_trimmed_string" | xargs) 
Newton_Jose
quelle
1
Dadurch werden auch mehrere Leerzeichen innerhalb einer Zeile zusammengezogen, die in der Frage
roaima
1
@roaima - wahr, aber die akzeptierte Antwort drückt auch Leerzeichen aus (was in der Frage nicht angefordert wurde). Ich denke, das eigentliche Problem hier ist, dass xargses nicht funktioniert, wenn die Eingabe Backslashes und einfache Anführungszeichen enthält.
don_crissti
@don_crissti bedeutet jedoch nicht, dass die akzeptierte Antwort die gestellte Frage korrekt beantwortet. In diesem Fall wurde dies jedoch nicht als Einschränkung gekennzeichnet, wohingegen dies in der akzeptierten Antwort der Fall war. Ich habe hoffentlich darauf hingewiesen, falls dies für einen zukünftigen Leser von Bedeutung ist.
Roaima
Es bricht auch bei einfachen Anführungszeichen, doppelten Anführungszeichen und Backslash-Zeichen. Es werden auch ein oder mehrere echoAufrufe ausgeführt. Einige Echo-Implementierungen verarbeiten auch Optionen und / oder Backslashes ... Das funktioniert auch nur bei einzeiliger Eingabe.
Stéphane Chazelas
17
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'

Wenn Sie eine Zeile in eine Shell-Variable einlesen, readgeschieht dies bereits, sofern nicht anders angegeben .

Gilles
quelle
1
+1 für read. Wenn Sie also beim Lesen eine Pipe verwenden, funktioniert cat file | while read i; do echo $i; done
Folgendes
1
@rubo mit der Ausnahme, dass in Ihrem Beispiel die Variable ohne Anführungszeichen ebenfalls von der Shell erneut verarbeitet wird. Verwenden Sie echo "$i"diese read
Option
13

Wenn Sie Zeilen als Variablen speichern, können Sie den Job mit bash erledigen:

Führende Leerzeichen aus einem String entfernen:

shopt -s extglob
echo ${text##+([[:space:]])}

Entfernen Sie nachfolgende Leerzeichen aus einem String:

shopt -s extglob
echo ${text%%+([[:space:]])}

Entfernen Sie alle Leerzeichen aus einer Zeichenfolge:

echo ${text//[[:space:]]}
Łukasz Rajchel
quelle
Das Entfernen aller Leerzeichen aus einer Zeichenfolge ist nicht dasselbe wie das Entfernen von führenden und nachfolgenden Leerzeichen (wie in Frage).
Catpnosis
Die weitaus beste Lösung - es werden nur Bash-Builtins und keine externen Prozessgabeln benötigt.
user259412
2
Nett. Skripte werden VIEL schneller ausgeführt, wenn sie keine externen Programme (wie awk oder sed) aufrufen müssen. Dies funktioniert auch mit "modernen" (93u +) Versionen von ksh.
user1683793
9

Um alle führenden und nachfolgenden Leerzeichen einer bestimmten Zeile mithilfe eines Pipe-Tools zu entfernen, kann ich drei verschiedene Wege identifizieren, die nicht vollständig gleichwertig sind. Diese Unterschiede betreffen die Leerzeichen zwischen den Wörtern der Eingabezeile. Abhängig vom erwarteten Verhalten treffen Sie Ihre Wahl.

Beispiele

Um die Unterschiede zu erklären, betrachten wir diese Dummy-Eingabezeile:

"   \t  A   \tB\tC   \t  "

tr

$ echo -e "   \t  A   \tB\tC   \t  " | tr -d "[:blank:]"
ABC

trist wirklich ein einfacher Befehl. In diesem Fall werden Leerzeichen oder Tabellierungszeichen gelöscht.

awk

$ echo -e "   \t  A   \tB\tC   \t  " | awk '{$1=$1};1'
A B C

awk löscht führende und abschließende Leerzeichen und drückt alle Leerzeichen zwischen Wörtern auf ein einzelnes Leerzeichen.

sed

$ echo -e "   \t  A   \tB\tC   \t  " | sed 's/^[ \t]*//;s/[ \t]*$//'
A       B   C

In diesem Fall werden sedführende und nachfolgende Leerzeichen gelöscht, ohne dass Leerzeichen zwischen Wörtern berührt werden.

Anmerkung:

Bei einem Wort pro Zeile trerledigt der Job.

frozar
quelle
Nichts davon trimmt jedoch nachfolgende / führende Zeilenumbrüche
hohe Wartung
+1 für eine Liste der Lösungen mit ihrer (manchmal unerwarteten) Ausgabe.
Tony
@ user61382 das ist ziemlich spät, aber siehe meinen Kommentar zum Originalbeitrag.
Tony
@highmaintenance: Verwenden Sie [:space:]anstelle von [: blank:] den Befehl trwie ... | tr -d [:space:]:, um auch Zeilenumbrüche zu entfernen. (siehe: man tr)
tron5
6

sed ist ein großartiges Werkzeug dafür:

                        # substitute ("s/")
sed 's/^[[:blank:]]*//; # parts of lines that start ("^")  with a space/tab 
     s/[[:blank:]]*$//' # or end ("$") with a space/tab
                        # with nothing (/)

Sie können es für Ihren Fall verwenden, indem Sie entweder den Text weiterleiten, z

<file sed -e 's/^[[...

oder indem du darauf 'inline' reagierst, wenn du seddie GNU bist:

sed -i 's/...' file

Das Ändern der Quelle auf diese Weise ist jedoch "gefährlich", da sie möglicherweise nicht wiederhergestellt werden kann, wenn sie nicht ordnungsgemäß funktioniert (oder sogar, wenn dies der Fall ist!). Sichern Sie also zuerst (oder verwenden Sie diese Methode -i.bak, um auf einige BSDs portierbar zu sein sed). !

Michael Durrant
quelle
2

Der Übersetzungsbefehl würde funktionieren

cat file | tr -d [:blank:]
Srinagesh
quelle
4
Dieser Befehl ist nicht korrekt, da alle Leerzeichen aus der Datei entfernt werden, nicht nur führende / nachfolgende Leerzeichen.
Brian Redbeard
@BrianRedbeard Du bist richtig. Dies ist immer noch eine nützliche Antwort für eine monolithische Zeichenfolge ohne Leerzeichen.
Anthony Rutledge
0

Wenn der zu trimmende String kurz und zusammenhängend ist, kann er einfach als Parameter an eine beliebige Bash-Funktion übergeben werden:

    trim(){
        echo $@
    }

    a="     some random string   "

    echo ">>`trim $a`<<"
Output
>>some random string<<
Subrata Das
quelle