Zamień wiele spacji na jedną, używając tylko „tr”

71

Mam plik f1.txt:

ID     Name
1      a
2         b
3   g
6            f

Liczba spacji nie jest stała. Jaki jest najlepszy sposób, aby zastąpić wszystkie białe spacje tylko jedną spacją tr?

Oto co mam do tej pory:

cat f1.txt | tr -d " "

Ale wynik jest następujący:

IDName
1a
2b
3g
6f

Ale chcę, aby wyglądało to tak:

ID Name
1 a
2 b
3 g
6 f

Staraj się unikać sed.

gkmohit
źródło
6
Dlaczego tak ważne jest unikanie sed? Używaj cokolwiek działa!
David Richerby
7
Ponieważ wiem, jak to zrobić sed. Chciałem poznać inne sposoby
:)

Odpowiedzi:

106

Za trpomocą opcji powtarzania squeeze :

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

Lub możesz użyć awkrozwiązania:

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

Po zmianie pola w rekordzie awkprzebuduj $0, zabiera wszystkie pola i łączy je razem, oddzielone OFSznakiem, który jest domyślnie spacją.

Spowoduje to wyciśnięcie sekwencji spacji i tabulatorów (i ewentualnie innych pustych znaków w zależności od ustawień regionalnych i implementacji awk) w jedną spację, ale również usunie początkowe i końcowe spacje z każdej linii.

Cuonglm
źródło
1
To także świetne rozwiązanie. . . Nie wiem, który wybrać teraz: / @Gnouc
gkmohit
Wybierz dowolne rozwiązanie, które Ci się spodoba. Uwaga: moje rozwiązanie różni się w odpowiedzi na @ polym.
cuonglm
1
:)) tak! Odpowiedź @Gnouca jest naprawdę dynamiczna, ponieważ używa awk, może zrobić wszystko. Możesz także zaakceptować jego rozwiązanie. Tylko jedna rzecz: Gnouc, czy możesz wyjaśnić, co robi format awk w twoim poleceniu? Czy możesz także dodać tabulatory / spacje, aby dane wyjściowe były zgodne z oczekiwanymi danymi wyjściowymi Nieznanego?
polym
1
@polym: Z ostatnią edycją Nieznanego wydaje się, że chce tylko jednej spacji, a nie danych wyjściowych jak column -trobi. Dodaj wyjaśnienie dla awk.
cuonglm
4
Jest tutaj niewielka różnica. trzastąpi dwie spacje na końcu linii pojedynczą spacją. awkusunie wszystkie spacje końcowe.
Anne van Rossum,
19

Po prostu użyj column:

column -t inputFile

Wynik:

ID  Name
1   a
2   b
3   g
6   f
polim
źródło
Cudowna i szybka odpowiedź :)
gkmohit
1
@Nieznany Świetnie być do usług :)!
polym
1
@Gnouc wow cool, kolumna przyjmuje również plik jako argument. fajnie dzięki!
polym
Jak mogę uzyskać drugą kolumnę tylko, jeśli chcę? Próbowałem column -t f1.txt | cut -d " " -f2 Ale nie było to rozwiązanie, którego się spodziewałem
gkmohit
2
Następnie użyj awk: column -t file | awk '{print $2}'drukuje tylko drugą kolumnę
polym
8

Jeśli chcesz ściśnąć „białą spację”, będziesz chciał użyć predefiniowanych zestawów znaków tr „: blank:” (pozioma biała spacja i spacja) lub „: space:” (verical biała spacja):

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

Przykłady uruchomiono na Red Hat 5 (GNU tr).

W moim przypadku chciałem znormalizować wszystkie białe spacje do jednej spacji, aby móc polegać na spacji jako delmiterze.

Jak wskazał drugi komentarz dastrobu, brakowało mi sformułowania na stronie podręcznika:

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

To pozwala nam wyeliminować pierwszy tr. Kudo będzie szukał swoich cierpliwości w obliczu mojej gęstości.

Wcześniej parsowanie portu z konfiguracji Redis. plik:

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

Po, z ustawieniem SET2 za pomocą ściśnięcia:

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

Wynik:

6379

Więcej informacji na temat niuansów białych znaków

Zademonstruj, gdzie samo ściśnięcie kończy się niepowodzeniem, gdy w grę wchodzą kolejne mieszane postacie należące do klasy znaków [: blank:]:

 /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

Uwaga: Moje dwa pola ciągów w formacie printf są oddzielone 1 spacją, 1 tabulacją, 1 spacją. Po ściśnięciu ta sekwencja wciąż istnieje. Na wyjściu zrzutu ósemkowego jest to reprezentowane przez sekwencję ascii 040 011 040.

użytkownik3183018
źródło
1
Czy naprawdę potrzebujesz tr "[:blank:]" " " | tr -s "[:blank:]"? Myślę, że pierwsza część wystarczy, tj. tr "[:blank:]" " "Ponieważ normalizuje białe spacje i dokonuje już podstawienia. Ze strony podręcznika: „Ściśnij wiele wystąpień znaków [...] Dzieje się tak po zakończeniu usuwania i tłumaczenia.”
dastrobu
2
więc ´tr -s "[: blank:]" "" "´ powinien to zrobić, najpierw tłumaczy wszystkie spacje na spacje, a następnie ściska spacje. Nie potrzeba drugiego „tr”.
dastrobu
1
Próbowałem printf 'ID \t Name\n' | tr -s "[:blank:]" " " | od -cb(jak sugeruje @dastrobu) i dostałem ID Name\n(z jedną spacją) jako wynik. Czy rzeczywiście próbowałeś, @ user3183018?
Scott
1
OK, spróbuję to powtórzyć. Zrobiłem printf 'ID␣\t␣Name\n' | tr -s "[:blank:]" "␣"  (jak sugeruje @dastrobu), gdzie reprezentuje spację, i dostałem ID␣Name\n(z jedną spacją) jako wynik. Jest to dokładnie to samo, co w przykładzie „Port <SPACE> <TAB> <SPACE> 6379”, z tym wyjątkiem, że użyłem ciągów nagłówka z pytania. Zastanawiam się, czy próbowałeś  tr -s "[:blank:]"(bez ostatniego "␣"argumentu).
Scott
1
Kiedy to robię printf 'ID \t Name\n' | od -cb, pokazuje dokładnie, co powinno: ID ⁠  \t ⁠  N a m e \n(tj  ID 040 011 040 N a m e\n.). Tymczasem, na podstawie własnych dowodów, popełniasz dokładnie taki błąd, jak się spodziewałem: działasz tr -s "[:blank:]"(tj.  trZ jedną opcją i  jednym argumentem), zamiast polecenia, które @dastrobu i ja przedstawiliśmy teraz cztery razy: tr -s '[:blank:]' '␣'(tj.  trz jedną opcją i  dwoma argumentami ).
Scott
5

Kto potrzebuje programu (innego niż powłoka)?

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

Jeśli chcesz, aby wartości w drugiej kolumnie były wyrównane, jak w columnodpowiedzi polimorficznej , użyj printfzamiast echo:

while read a b
do
    printf '%-2s %s\n' "$a" "$b"
done < f1.txt
Scott
źródło
1
Po pierwsze, w porównaniu z tr - jest to strasznie słaba sugestia pod względem wydajności, chyba że dane wejściowe są zbyt małe, aby przewyższyć niewielki koszt trwywołania - nie wspominając już o tym, ile więcej pracy zajmuje napisanie. Na koniec, czy nie powiedziałbyś, że ten post nie odpowiada na zadane pytanie? Jaki jest najlepszy sposób zastąpienia wszystkich białych spacji jedną spacją za pomocą tylko tr?
mikeserv
1
A poza tym - czy nie mógłbyś łatwiej po prostu coś zrobić $IFS? Może jak IFS=' <tab>' set -f ; echo $(cat <file):?
mikeserv
2

To stare pytanie i rozwiązane wiele razy. Tylko dla kompletności: miałem problem z symulacją, ale chciałem przekazać linie rurą do programu antother. Użyłem 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.

więc cat f1.txt | xargs -L1wydaje się, że generuje dokładnie to, czego chcesz.

Martin Seitl
źródło