Jak użyć polecenia powłoki, aby wyświetlić tylko pierwszą kolumnę i ostatnią kolumnę w pliku tekstowym?

30

Potrzebuję pomocy, aby dowiedzieć się, jak użyć polecenia sed, aby wyświetlić tylko pierwszą kolumnę i ostatnią kolumnę w pliku tekstowym. Oto, co do tej pory mam dla kolumny 1:

cat logfile | sed 's/\|/ /'|awk '{print $1}'

Moja słaba próba pokazania ostatniej kolumny również:

cat logfile | sed 's/\|/ /'|awk '{print $1}{print $8}'

Jednak to bierze pierwszą kolumnę i ostatnią kolumnę i łączy je razem w jedną listę. Czy istnieje sposób na wyraźne wydrukowanie pierwszej kolumny i ostatnich kolumn za pomocą poleceń sed i awk?

Przykładowe dane wejściowe:

foo|dog|cat|mouse|lion|ox|tiger|bar
użytkownik70573
źródło
5
Podaj przykładowe dane wejściowe.
jasonwryan

Odpowiedzi:

51

Prawie na miejscu. Po prostu umieść oba odniesienia do kolumn obok siebie.

cat logfile | sed 's/|/ /' | awk '{print $1, $8}'

Pamiętaj też, że nie potrzebujesz cattutaj.

sed 's/|/ /' logfile | awk '{print $1, $8}'

Zauważ też, że możesz powiedzieć, awkże separatory kolumn są |zamiast pustych, więc nie potrzebujesz sedżadnego z nich.

awk -F '|' '{print $1, $8}' logfile

Zgodnie z sugestiami przez Kaleba , jeśli chcesz rozwiązania, które nadal wyprowadza ostatnie pole, nawet jeśli nie są dokładnie osiem, można użyć $NF.

awk -F '|' '{print $1, $NF}' logfile

Ponadto, jeśli chcesz, aby wyjście zachowało |separatory, zamiast spacji, możesz określić separatory pól wyjściowych. Niestety jest to nieco bardziej niezręczne niż używanie -Fflagi, ale oto trzy podejścia.

  • Możesz przypisać separatory pól wejściowych i wyjściowych w awksobie, w bloku BEGIN.

    awk 'BEGIN {FS = OFS = "|"} {print $1, $8}' logfile
  • Możesz przypisać te zmienne podczas wywoływania awkz linii poleceń, poprzez -vflagę.

    awk -v 'FS=|' -v 'OFS=|' '{print $1, $8}' logfile
  • lub po prostu:

    awk -F '|' '{print $1 "|" $8}' logfile
Krogulec
źródło
4
Dobra robota w podziale, w jaki sposób ten problem można uprościć. Możesz dodać notatkę o tym, jak używać |jako separatora wyjściowego zamiast domyślnej przestrzeni do łączenia łańcuchów. Możesz także wyjaśnić, jak używać $NFzamiast kodowania $8na stałe, aby uzyskać ostatnią kolumnę.
Caleb
12

Po prostu zamień od pierwszego do ostatniego |na |(lub spację, jeśli wolisz):

sed 's/|.*|/|/'

Zauważ, że chociaż nie ma sedimplementacji, która |jest wyjątkowa (o ile rozszerzone wyrażenia regularne nie są włączane za pośrednictwem -Elub -rw niektórych implementacjach), \|sama jest wyjątkowa w niektórych takich jak GNU sed. Więc powinien nie uciec |, jeśli zamierzają go dopasować |charakter.

Jeśli zastąpisz spacją i jeśli dane wejściowe mogą już zawierać wiersze z jednym |, wtedy będziesz musiał potraktować to specjalnie, ponieważ |.*|nie będzie pasować do nich. To mogłoby być:

sed 's/|\(.*|\)\{0,1\}/ /'

(to znaczy, że .*|część jest opcjonalna) Lub:

sed 's/|.*|/ /;s/|/ /'

lub:

sed 's/\([^|]*\).*|/\1 /'

Jeśli chcesz pierwsze i ósme pole niezależnie od liczby pól w danych wejściowych, to po prostu:

cut -d'|' -f1,8


(wszystkie te działałyby z dowolnym narzędziem zgodnym z POSIX, zakładając, że dane wejściowe tworzą poprawny tekst (w szczególności sedte nie będą działać, jeśli dane wejściowe zawierają bajty lub sekwencje bajtów, które nie tworzą prawidłowych znaków w bieżącym języku, na przykład printf 'unix|St\351phane|Chazelas\n' | sed 's/|.*|/|/'w ustawienia regionalne UTF-8)).

Stéphane Chazelas
źródło
11

Tak czy awkinaczej używasz :

awk '{ print $1, $NF }' file
jasonwryan
źródło
2
Czy nie musisz określać separatora pól wejściowych (ponieważ w tym przypadku wydaje się |, że to raczej spacja) za pomocą -F\|lub podobnego? A co, jeśli chciałby użyć tego samego separatora dla danych wyjściowych?
Caleb
@Caleb Prawdopodobnie: Czekałem, aż OP potwierdzi, jak dokładnie wyglądał wkład, zamiast próbować zgadywać na podstawie niedziałających przykładów ...
jasonwryan
1
Zauważ, że przy założeniu, że dane wejściowe zawierają co najmniej 2 pola.
Stéphane Chazelas,
@ StéphaneChazelas OP jasno stwierdził w kodzie, że zawsze ma osiem pól.
michaelb958 - Przywróć Monikę
3
@ michaelb958 Myślę, że „wyraźnie” przesadza ze sprawą, tylko trochę :)
jasonwryan
4

Jeśli czujesz się nieswojo i bez sedna, możesz osiągnąć to samo z Coreutils:

paste <(           cut -d'|' -f1  file) \ 
      <(rev file | cut -d'|' -f1 | rev)
Thor
źródło
cutjest czystszy i bardziej kompaktowy niż awk / sed, gdy interesuje Cię tylko pierwsza kolumna lub jeśli ograniczniki są stałe (tj. nie zmienna liczba spacji).
Sridhar Sarnobat
2

Wygląda na to, że próbujesz uzyskać pierwsze i ostatnie pola tekstu, które są oddzielone |.

Zakładam, że twój plik dziennika zawiera tekst jak poniżej,

foo|dog|cat|mouse|lion|ox|tiger|bar
bar|dog|cat|mouse|lion|ox|tiger|foo

I chcesz, aby wynik był jak

foo bar
bar foo

Jeśli tak, oto polecenie dla twojego

Poprzez GNU sed,

sed -r 's~^([^|]*).*\|(.*)$~\1 \2~' file

Przykład:

$ echo 'foo|dog|cat|mouse|lion|ox|tiger|bar' | sed -r 's~^([^|]*).*\|(.*)$~\1 \2~'
foo bar
Avinash Raj
źródło
Kolumny nie są rozdzielane przez potok | ale są w kolumnach, jestem zainteresowany użyciem sed, ale nie używam polecenia awk, tak jak to zrobiłeś w swoim poleceniu: sed -r's ~ ^ ([^ |] *). * \ | (. *) $ ~ \ Plik 1 \ 2 ~ '
użytkownik70573
„Kolumny nie są oddzielone rurą | ale są w kolumnach”, masz na myśli, że kolumny są oddzielone spacjami?
Avinash Raj
Próbka wejściowa i wyjściowa byłyby lepsze.
Avinash Raj
1

Prawdopodobnie powinieneś to zrobić sed- i tak bym to zrobił - ale dlatego, że nikt jeszcze tego nie napisał:

while IFS=\| read col1 cols
do  printf %10s%-s\\n "$col1 |" " ${cols##*|}"
done <<\INPUT
foo|dog|cat|mouse|lion|ox|tiger|bar
INPUT

WYDAJNOŚĆ

     foo | bar
mikeserv
źródło