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 

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)


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


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)


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) 

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 .


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:]]}

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.


Ż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) !


2

polecenie translacji działałoby

cat file | tr -d [:blank:]

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<<
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.