Jak przyciąć wiodące i końcowe białe spacje z każdej linii niektórych wyników?

153

Chciałbym usunąć wszystkie początkowe i końcowe spacje i tabulatory z każdego wiersza wyniku.

Czy istnieje proste narzędzie, do trimktórego mógłbym przesłać dane wyjściowe?

Przykładowy plik:

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
źródło
1
Dla każdego, kto szuka tutaj rozwiązania do usuwania nowych linii, jest to inny problem. Z definicji nowa linia tworzy nową linię tekstu. Dlatego wiersz tekstu nie może zawierać nowego wiersza. Pytanie, które chcesz zadać, to jak usunąć nowy wiersz na początku lub na końcu łańcucha: stackoverflow.com/questions/369758 lub jak usunąć puste linie lub linie, które są po prostu białymi znakami: serverfault.com/questions/252921
Tony

Odpowiedzi:

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

lub krócej:

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

Przycinałby początkowe i końcowe spacje lub znaki tabulacji 1, a także ściskał sekwencje tabulatorów i spacji w jednym miejscu.

Działa to, ponieważ kiedy przypisujesz coś do jednego z pól , awkodbudowuje cały rekord (zgodnie z drukowaniem print), łącząc wszystkie pola ( $1, ..., $NF) z OFS(domyślnie spacja).

1 (i ewentualnie inne puste znaki w zależności od ustawień regionalnych i awkimplementacji)

Stéphane Chazelas
źródło
2
Średnik w drugim przykładzie jest zbędny. Można użyć:awk '{$1=$1}1'
Brian
Ciekawe ... Żadne średniki nie są obsługiwane przez gawk, mawk i awk OS X. (Przynajmniej dla moich wersji (odpowiednio 1.2, 4.1.1 i 20070501)
Brian
1
Jedyne, co mi się nie podoba w tym podejściu, to to, że tracisz powtarzające się spacje w linii. Na przykładecho -e 'foo \t bar' | awk '{$1=$1};1'
przyjazny dla użytkownika
2
echo ' hello ' | xargs
JREAM
43

Polecenie można tak skondensować, jeśli używasz GNU sed:

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

Przykład

Oto powyższe polecenie w akcji.

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

Możesz użyć, hexdumpaby potwierdzić, że sedpolecenie poprawnie usuwa żądane znaki.

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

Klasy postaci

Możesz także użyć nazw klas znaków zamiast dosłownie wymieniać takie zestawy [ \t]:

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

Przykład

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

Większość narzędzi GNU korzystających z wyrażeń regularnych (regex) obsługuje te klasy.

 [[: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

Używanie ich zamiast zestawów literalnych zawsze wydaje się marnowaniem miejsca, ale jeśli martwisz się, że Twój kod jest przenośny lub masz do czynienia z alternatywnymi zestawami znaków (myśl międzynarodowymi), prawdopodobnie będziesz chciał użyć nazw klas zamiast.

Bibliografia

slm
źródło
Zauważ, że [[:space:]]nie jest to równoważne [ \t]z ogólnym przypadkiem (Unicode itp.). [[:space:]]prawdopodobnie będzie znacznie wolniejszy (ponieważ w Unicode jest o wiele więcej rodzajów białych znaków niż tylko ' 'i '\t'). To samo dotyczy wszystkich pozostałych.
Olivier Dulac
sed 's/^[ \t]*//'nie jest przenośny. W rzeczywistości POSIX wymaga nawet usunięcia sekwencji spacji, ukośnika odwrotnego lub tznaków, i to właśnie robi GNU sed, gdy POSIXLY_CORRECTjest w środowisku.
Stéphane Chazelas,
Co jeśli chcę przyciąć znaki nowego wiersza? '\ n \ n tekst \ n \ n'
Eugene Biryukov
Lubię rozwiązanie sed z powodu braku innych skutków ubocznych, jak w rozwiązaniu awk. Pierwsza odmiana nie działa, gdy wypróbowałem ją teraz w bash na OSX jsut, ale wersja klasy postaci działa:sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//'
Tony
@EugeneBiryukov zobacz mój komentarz do oryginalnego postu
Tony
23

Jak sugeruje Stéphane Chazelas w przyjętej odpowiedzi, możesz teraz
utworzyć skrypt /usr/local/bin/trim:

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

i nadaj temu plikowi uprawnienia do wykonywania:

chmod +x /usr/local/bin/trim

Teraz możesz przekazać każde wyjście trimna przykład:

cat file | trim

(w przypadku komentarzy poniżej: użyłem tego wcześniej: while read i; do echo "$i"; done
co również działa dobrze, ale jest mniej wydajne)

rubo77
źródło
1
Powodzenia, jeśli plik jest ogromny i / lub zawiera ukośniki odwrotne.
don_crissti
1
@don_crissti: czy mógłbyś skomentować nieco więcej ?, które rozwiązanie byłoby bardziej odpowiednie dla dużych plików i jak mogę zmodyfikować moje rozwiązanie, jeśli plik zawiera ukośniki odwrotne?
rubo77
3
Będziesz musiał użyć while read -r linedo zachowania backslashy i nawet wtedy ... . Jeśli chodzi o ogromne pliki / szybkość, naprawdę wybrałeś najgorsze rozwiązanie. Nie sądzę, żeby było tam coś gorszego. Zobacz odpowiedzi na pytanie: Dlaczego używanie pętli powłoki do przetwarzania tekstu jest złą praktyką? w tym mój komentarz do ostatniej odpowiedzi, w której dodałem link do testu porównawczego prędkości. Te sedtutaj odpowiedzi są perfekcyjnie IMO i znacznie lepiej niż read.
don_crissti
@don_crissti ... i / lub ma wiersze rozpoczynające się od -i po nich kombinacjami 1 lub więcej znaków e, E lub n i / lub zawiera znaki NUL. Również linia nieskończona po ostatniej nowej linii zostanie pominięta.
Stéphane Chazelas
1
Możesz także dodać alias w / etc / profile (lub w swoim ~ / .bashrc lub ~ / .zshrc itp.) Alias ​​trim = "awk '{\ $ 1 = \ $ 1}; 1'"
Jeff Clayton
22

xargs bez argumentów to robi.

Przykład:

trimmed_string=$(echo "no_trimmed_string" | xargs) 
Newton_Jose
źródło
1
Spowoduje to również zawarcie wielu spacji w linii, o które nie pytano w pytaniu
roaima,
1
@roaima - prawda, ale zaakceptowana odpowiedź ściska spacje (które nie były wymagane w pytaniu). Myślę, że prawdziwym problemem jest to, że xargsnie uda się dostarczyć, jeśli dane wejściowe zawierają odwrotne ukośniki i pojedyncze cudzysłowy.
don_crissti
@don_crissti, co nie oznacza, że ​​zaakceptowana odpowiedź poprawnie odpowiada na zadane pytanie. Ale w tym przypadku nie zostało to oznaczone jako zastrzeżenie, podczas gdy w przyjętej odpowiedzi było inaczej. Mam nadzieję, że podkreśliłem ten fakt, jeśli ma to znaczenie dla przyszłego czytelnika.
roaima,
Działa również na pojedyncze cudzysłowy, podwójne cudzysłowy, znaki odwrotnego ukośnika. Uruchamia także jedną lub więcej echowywołań. Niektóre implementacje echa przetwarzają również opcje i / lub ukośniki odwrotne ... Działa to również tylko w przypadku wprowadzania jednowierszowego.
Stéphane Chazelas
17
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'

Jeśli czytasz wiersz w zmiennej powłoki, readrób to już, chyba że instrukcja inaczej .

Gilles
źródło
1
+1 dla read. Jeśli więc cat file | while read i; do echo $i; done
odczytasz
1
@rubo oprócz tego, że w twoim przykładzie niecytowana zmienna jest również przetwarzana przez powłokę. Użyj, echo "$i"aby zobaczyć prawdziwy efektread
roaima
13

Jeśli przechowujesz linie jako zmienne, możesz użyć bash do wykonania zadania:

usuń wiodące białe znaki z ciągu:

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

usuń końcowe białe znaki z łańcucha:

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

usuń wszystkie białe znaki z łańcucha:

echo ${text//[[:space:]]}
Łukasz Rajchel
źródło
Usunięcie wszystkich białych znaków z łańcucha nie jest tym samym, co usunięcie zarówno początkowych, jak i końcowych spacji (jak w pytaniu).
catpnosis
Zdecydowanie najlepsze rozwiązanie - wymaga tylko wbudowanych bashów i żadnych zewnętrznych procesorów.
peterh
2
Miły. Skrypty działają DUŻO szybciej, jeśli nie muszą pobierać programów zewnętrznych (takich jak awk lub sed). Działa to również z „nowoczesnymi” (93u +) wersjami ksh.
user1683793,
9

Aby usunąć wszystkie początkowe i końcowe spacje z danej linii dzięki narzędziu „potokowemu”, mogę zidentyfikować 3 różne sposoby, które nie są całkowicie równoważne. Różnice te dotyczą odstępów między słowami linii wejściowej. W zależności od oczekiwanego zachowania dokonasz wyboru.

Przykłady

Aby wyjaśnić różnice, rozważmy następujący fikcyjny wiersz wejściowy:

"   \t  A   \tB\tC   \t  "

tr

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

trjest naprawdę prostym poleceniem. W takim przypadku usuwa wszelkie spacje lub znaki tabulacji.

awk

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

awk usuwa spacje wiodące i tailing i ściska do pojedynczego spacji co spację między słowami.

sed

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

W takim przypadku sedusuwa spacje początkowe i końcowe bez dotykania spacji między słowami.

Uwaga:

W przypadku jednego słowa w wierszu trwykonuje zadanie.

Frozar
źródło
Żadne z tych elementów nie przycina jednak końcowych / wiodących nowych linii
highmaintenance
+1 za listę rozwiązań z (czasem nieoczekiwanymi) wynikami.
Tony
@ user61382 jest raczej późno, ale zobacz mój komentarz do oryginalnego postu.
Tony
@highmaintenance: użyj [:space:]zamiast polecenia [: blank:] dla polecenia tr, takiego jak :, ... | tr -d [:space:]aby usunąć również nowe wiersze. (patrz: man tr)
tron5
6

sed jest doskonałym narzędziem do tego:

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

Możesz go użyć w swoim przypadku, albo wstawiając tekst, np

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

lub działając na nim „inline”, jeśli twój sedjest GNU:

sed -i 's/...' file

ale zmiana źródła w ten sposób jest „niebezpieczna”, ponieważ może być niemożliwa do odzyskania, gdy nie działa poprawnie (lub nawet gdy działa!), więc najpierw wykonaj kopię zapasową (lub użyj, -i.bakktóry ma tę zaletę, że może być przenośny na niektóre BSD sed) !

Michael Durrant
źródło
2

polecenie translacji działałoby

cat file | tr -d [:blank:]
Srinagesh
źródło
4
To polecenie jest niepoprawne, ponieważ usuwa wszystkie spacje z pliku, a nie tylko początkowe / końcowe białe znaki.
Brian Redbeard
@BrianRedbeard Masz rację. Jest to nadal przydatna odpowiedź na monolityczny ciąg bez spacji.
Anthony Rutledge
0

Jeśli ciąg, który próbujesz przyciąć, jest krótki i ciągły / ciągły, możesz po prostu przekazać go jako parametr do dowolnej funkcji bash:

    trim(){
        echo $@
    }

    a="     some random string   "

    echo ">>`trim $a`<<"
Output
>>some random string<<
Subrata Das
źródło