Używanie awk do drukowania wszystkich kolumn od n-tego do ostatniego


310

Ta linia działała, dopóki nie miałem białych znaków na drugim polu.

svn status | grep '\!' | gawk '{print $2;}' > removedProjs

czy istnieje sposób, aby awk wydrukował wszystko za 2 USD lub więcej? (3 USD, 4 USD ... dopóki nie będziemy już mieć kolumn?)

Powinienem dodać, że robię to w środowisku Windows z Cygwin.


11
grep | awkNawiasem mówiąc , jest to antypattern - chceszawk '/!/ { print $2 }'
potrójny

3
Uniksowe „cięcie” jest łatwiejsze ...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
roblogic

Możliwe duplikaty reszty wydruku w awk
acm

@tripleee: Cieszę się, że o tym wspomniałeś - jestem sfrustrowany widząc to wszędzie!
Graham Nicholls,

Odpowiedzi:


489

wypisze wszystkie oprócz pierwszej kolumny:

awk '{$1=""; print $0}' somefile

wydrukuje wszystkie oprócz dwóch pierwszych kolumn:

awk '{$1=$2=""; print $0}' somefile

93
gotcha: pozostawia wiodącą przestrzeń zwisającą wokół :(
raphinesse

5
lubię pragmatyczne podejście. nie trzeba jednak używać cat, wystarczy wpisać nazwę pliku po komendzie awk.
kon

45
@raphinesse możesz to naprawić za pomocąawk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo 12.09.13

6
Nie działa to z ogranicznikami spacjami, zastępuje je spacją.
Dejan

3
W przypadku ograniczników spacji można określić Separator pól wyjściowych (OFS), np. Przecinek: awk -F, -vOFS=, '{$1=""; print $0}'Otrzymasz początkowy separator ( $1nadal jest zawarty, podobnie jak pusty ciąg). Możesz to rozebrać za pomocą sed:awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
cherdt

99

Istnieje zduplikowane pytanie z prostszą odpowiedzią przy użyciu cut:

 svn status |  grep '\!' | cut -d\  -f2-

-dokreśla ogranicznik (spację) , -fokreśla listę kolumn (wszystkie zaczynające się na 2.)


Możesz także użyć „-b”, aby określić pozycję (od n-tego znaku).
Dakatine,

Uwaga: chociaż wykonuje to samo zadanie co awkwersja, występują problemy z buforowaniem linii cut, które awknie mają: stackoverflow.com/questions/14360640/…
sdaau

24
Ładne i proste, ale zawiera zastrzeżenie: awktraktuje wiele sąsiednich znaków kosmicznych. jako pojedynczy separator, podczas gdy cutnie; także - chociaż nie jest to problem w niniejszej sprawie - cutakceptuje tylko jeden, dosłowny znak. jako separator, a jednocześnie awkpozwala na wyrażenie regularne.
mklement0

W oparciu o to: stackoverflow.com/a/39217130/8852408 , prawdopodobne jest, że to rozwiązanie nie jest bardzo wydajne.
FcknGioconda

85

Możesz użyć pętli for, aby przejść przez pola drukowania od 2 $ do $ NF (wbudowana zmienna reprezentująca liczbę pól w linii).

Edycja: Ponieważ „print” dodaje nowy wiersz, będziesz chciał buforować wyniki:

awk '{out=""; for(i=2;i<=NF;i++){out=out" "$i}; print out}'

Alternatywnie użyj printf:

awk '{for(i=2;i<=NF;i++){printf "%s ", $i}; printf "\n"}'

Więc próbowałem tego, ale myślę, że coś mi umknęło .. oto, co zrobiłem svn status | grep „\!” | gawk '{for (i = 1; i <= $ NF; i ++) print $ i "";}'> removeProjs
Andy

Ponieważ print dołącza nowy wiersz, będziesz chciał buforować wyniki. Zobacz moją edycję.
VeeArr

1
Bardziej podoba mi się ta odpowiedź, ponieważ pokazuje, jak przewijać pola.
Edward Falk

3
Jeśli chcesz, aby print używał spacji, zmień separator rekordów wyjściowych: awk '{ORS = ""; for (i = 2; i <NF; i ++) print $ i} 'somefile
Christian Lescuyer

3
Zawsze będzie za dużo miejsca. Działa to lepiej: '{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'brak spacji wiodących i końcowych.
Marki

24
awk '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}'

Moja odpowiedź jest oparta na jednej z VeeArr , ale zauważyłem, że zaczęła się od białej spacji, zanim wydrukuje drugą kolumnę (i resztę). Ponieważ mam tylko 1 punkt reputacji, nie mogę go komentować, więc oto nowa odpowiedź:

zacznij od „out” jako drugiej kolumny, a następnie dodaj wszystkie pozostałe kolumny (jeśli istnieją). To idzie dobrze, o ile istnieje druga kolumna.


2
Doskonale, usunąłeś również $ przed zmienną out, co również jest ważne.
Alexis Wilke

15

Większość rozwiązań z awk pozostawia miejsce. Opcje tutaj unikają tego problemu.

opcja 1

Proste rozwiązanie cięcia (działa tylko z pojedynczymi ogranicznikami):

command | cut -d' ' -f3-

Opcja 2

Wymuszanie ponownego obliczenia awk czasami usuwa dodatkową przestrzeń wiodącą (OFS) pozostałą przez usunięcie pierwszych pól (działa z niektórymi wersjami awk):

command | awk '{ $1=$2="";$0=$0;} NF=NF'

Opcja 3

Drukowanie każdego pola sformatowanego za pomocą printfdaje większą kontrolę:

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in"|awk -v n=2 '{ for(i=n+1;i<=NF;i++) printf("%s%s",$i,i==NF?RS:OFS);}'
3 4 5 6 7 8

Jednak wszystkie poprzednie odpowiedzi zmieniają wszystkie powtarzane FS między polami na OFS. Zbudujmy kilka opcji, które tego nie robią.

Opcja 4 (zalecana)

Pętla z sub do usuwania pól i ograniczników z przodu.
I używając wartości FS zamiast spacji (którą można zmienić).
Jest bardziej mobilny, a nie powoduje zmiany FS OFS: UWAGA:^[FS]* jest zaakceptować wejście z czołowych miejsc.

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in" | awk '{ n=2; a="^["FS"]*[^"FS"]+["FS"]+";
  for(i=1;i<=n;i++) sub( a , "" , $0 ) } 1 '
3     4   5   6 7     8

Opcja 5

Jest całkiem możliwe zbudowanie rozwiązania, które nie dodaje dodatkowych (początkowych lub końcowych) białych znaków i zachowuje istniejące białe znaki za pomocą funkcji gensubz GNU awk, ponieważ:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
          { print(gensub(a""b""c,"",1)); }'
3     4   5   6 7     8 

Można go również użyć do zamiany grupy pól z podaną liczbą n:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
          {
            d=gensub(a""b""c,"",1);
            e=gensub("^(.*)"d,"\\1",1,$0);
            print("|"d"|","!"e"!");
          }'
|3     4   5   6 7     8  | !    1    2  !

Oczywiście w takim przypadku OFS służy do oddzielenia obu części linii, a końcowa biała przestrzeń pól jest nadal drukowana.

UWAGA: [FS]* służy do dopuszczenia spacji wiodących w linii wejściowej.


13

Osobiście wypróbowałem wszystkie wyżej wymienione odpowiedzi, ale większość z nich była nieco złożona lub po prostu nie tak. Z mojego punktu widzenia najłatwiej to zrobić:

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. Gdzie -F „” definiuje ogranicznik, którego ma używać awk. W moim przypadku jest to biały znak, który jest również domyślnym ogranicznikiem dla awk. Oznacza to, że -F „” można zignorować.

  2. Gdzie NF określa całkowitą liczbę pól / kolumn. Dlatego pętla rozpocznie się od 4. pola do ostatniego pola / kolumny.

  3. Gdzie $ N pobiera wartość N-tego pola. Dlatego print $ i wydrukuje bieżące pole / kolumnę na podstawie liczby pętli.


4
Problem, który drukuje każde pole w innej linii.
mveroone

nic nie powstrzymuje Cię przed dodaniem tego na końcu :-) `| tr '\ n' ''
koullislp

3
Trochę późno, ale awk '{for (i = 5; i <= NF; i ++) {printf "% s", $ i}}'
plitter

8
awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }'

lauhub proponuje rozwiązać ten problem, proste i szybkie rozwiązanie tutaj


7

Tak bardzo mnie to irytowało, że usiadłem i napisałem cutparser specyfikacji pola, przetestowany z GNU Awk 3.1.7.

Najpierw utwórz nowy skrypt biblioteki Awk o nazwie pfcut, np

sudo nano /usr/share/awk/pfcut

Następnie wklej poniższy skrypt i zapisz. Następnie wygląda to tak:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-4"); }'
t1 t2 t3 t4

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("2-"); }'
t2 t3 t4 t5 t6 t7

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

Aby uniknąć wpisywania tego wszystkiego, myślę, że najlepiej jak potrafisz (zobacz inaczej Automatycznie ładuj funkcję użytkownika podczas uruchamiania z awk? - Unix i Linux Stack Exchange ) dodaje alias do ~/.bashrc; np. z:

$ echo "alias awk-pfcut='awk -f pfcut --source'" >> ~/.bashrc
$ source ~/.bashrc     # refresh bash aliases

... wtedy możesz po prostu zadzwonić:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk-pfcut '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

Oto źródło pfcutskryptu:

# pfcut - print fields like cut
#
# sdaau, GNU GPL
# Nov, 2013

function spfcut(formatstring)
{
  # parse format string
  numsplitscomma = split(formatstring, fsa, ",");
  numspecparts = 0;
  split("", parts); # clear/initialize array (for e.g. `tail` piping into `awk`)
  for(i=1;i<=numsplitscomma;i++) {
    commapart=fsa[i];
    numsplitsminus = split(fsa[i], cpa, "-");
    # assume here a range is always just two parts: "a-b"
    # also assume user has already sorted the ranges
    #print numsplitsminus, cpa[1], cpa[2]; # debug
    if(numsplitsminus==2) {
     if ((cpa[1]) == "") cpa[1] = 1;
     if ((cpa[2]) == "") cpa[2] = NF;
     for(j=cpa[1];j<=cpa[2];j++) {
       parts[numspecparts++] = j;
     }
    } else parts[numspecparts++] = commapart;
  }
  n=asort(parts); outs="";
  for(i=1;i<=n;i++) {
    outs = outs sprintf("%s%s", $parts[i], (i==n)?"":OFS); 
    #print(i, parts[i]); # debug
  }
  return outs;
}

function pfcut(formatstring) {
  print spfcut(formatstring);
}

Wygląda na to, że chcesz użyć cut, a nieawk
roblogic

5

Wydruk kolumn zaczynających się od nr 2 (na początku nie będzie kończyć się spacją):

ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'

1
Fajnie, choć należy dodać +po spacji, ponieważ pola mogą być oddzielone więcej niż 1 spacją ( awktraktuje wiele sąsiadujących spacji jako pojedynczy separator). Ponadto awkzignoruje spacje wiodące, więc powinieneś zacząć od wyrażenia regularnego ^[ ]*. Dzięki spacji jako separatorowi możesz nawet uogólnić rozwiązanie; np. następujące zwraca wszystko z trzeciego pola: awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'Staje się jednak trudniejsze z dowolnymi separatorami pól.
mklement0

5

Czy to zadziała?

awk '{print substr($0,length($1)+1);}' < file

Pozostawia jednak trochę białych znaków z przodu.


4
echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}'

ten używa awk do wypisania wszystkich oprócz ostatniego pola


3

Oto, co wolałem od wszystkich zaleceń:

Drukowanie od 6 do ostatniej kolumny.

ls -lthr | awk '{out=$6; for(i=7;i<=NF;i++){out=out" "$i}; print out}'

lub

ls -lthr | awk '{ORS=" "; for(i=6;i<=NF;i++) print $i;print "\n"}'

2

Jeśli potrzebujesz konkretnych kolumn wydrukowanych z dowolnym ogranicznikiem:

awk '{print $3 "  " $4}'

col # 3 col # 4

awk '{print $3 "anything" $4}'

col # 3anythingcol # 4

Więc jeśli masz białą spację w kolumnie, będą to dwie kolumny, ale możesz połączyć ją z dowolnym separatorem lub bez niego.


2

Rozwiązanie Perla:

perl -lane 'splice @F,0,1; print join " ",@F' file

Używane są następujące opcje wiersza polecenia:

  • -n pętli wokół każdej linii pliku wejściowego, nie drukuj automatycznie każdej linii

  • -l usuwa nowe linie przed przetwarzaniem i dodaje je z powrotem

  • -atryb autosplit - dzieli linie wejściowe na tablicę @F. Domyślnie podział na białe znaki

  • -e wykonaj kod perla

splice @F,0,1 czysto usuwa kolumnę 0 z tablicy @F

join " ",@F łączy elementy tablicy @F, używając spacji między każdym elementem


Rozwiązanie Python:

python -c "import sys;[sys.stdout.write(' '.join(line.split()[1:]) + '\n') for line in sys.stdin]" < file


1

Jeśli nie chcesz ponownie sformatować tej części wiersza, której nie odetniesz, najlepsze rozwiązanie, jakie mogę wymyślić, znajduje się w mojej odpowiedzi w:

Jak wydrukować wszystkie kolumny po określonej liczbie za pomocą awk?

Wycina to, co jest przed danym polem o numerze N, i drukuje całą resztę wiersza, w tym pole o numerze N i zachowując oryginalne odstępy (nie jest ponownie formatowane). Nie ma znaczenia, czy ciąg pola pojawia się również gdzieś w linii.

Zdefiniuj funkcję:

fromField () { 
awk -v m="\x01" -v N="$1" '{$N=m$N; print substr($0,index($0,m)+1)}'
}

I użyj tego w ten sposób:

$ echo "  bat   bi       iru   lau bost   " | fromField 3
iru   lau bost   
$ echo "  bat   bi       iru   lau bost   " | fromField 2
bi       iru   lau bost 

Wyjście zachowuje wszystko, w tym końcowe spacje

W twoim konkretnym przypadku:

svn status | grep '\!' | fromField 2 > removedProjs

Jeśli twój plik / strumień nie zawiera znaków nowej linii na środku linii (możesz użyć innego separatora rekordów), możesz użyć:

awk -v m="\x0a" -v N="3" '{$N=m$N ;print substr($0, index($0,m)+1)}'

Pierwszy przypadek zawiedzie tylko w plikach / strumieniach zawierających rzadki znak szesnastkowy 1


0

Działa to, jeśli używasz Bash i możesz użyć tylu „x” jako elementów, które chcesz odrzucić, a ignoruje wiele spacji, jeśli nie są one poprzedzone znakiem ucieczki.

while read x b; do echo "$b"; done < filename

0

Perl:

@m=`ls -ltr dir | grep ^d | awk '{print \$6,\$7,\$8,\$9}'`;
foreach $i (@m)
{
        print "$i\n";

}

1
To nie odpowiada na pytanie, które uogólnia wymóg drukowania od N-tej kolumny do końca .
roaima,

0

Ta awkfunkcja zwraca podłańcuch tego, $0który zawiera pola od begindo end:

function fields(begin, end,    b, e, p, i) {
    b = 0; e = 0; p = 0;
    for (i = 1; i <= NF; ++i) {
        if (begin == i) { b = p; }
        p += length($i);
        e = p;
        if (end == i) { break; }
        p += length(FS);
    }
    return substr($0, b + 1, e - b);
}

Aby uzyskać wszystko, zaczynając od pola 3:

tail = fields(3);

Aby uzyskać sekcję, $0która obejmuje pola od 3 do 5:

middle = fields(3, 5);

b, e, p, ibzdury na liście parametrów funkcji to tylko awksposób na zadeklarowanie zmiennych lokalnych.


0

Chcę rozszerzyć proponowane odpowiedzi na sytuację, w której pola są ograniczone prawdopodobnie kilkoma białymi spacjami - powód, dla którego PO nie używacut przypuszczam, że .

Wiem, że o OP pytano awk, ale sedtutaj zadziałałoby podejście (przykład z drukowaniem kolumn od piątego do ostatniego):

  • podejście czysto sed

    sed -r 's/^\s*(\S+\s+){4}//' somefile

    Wyjaśnienie:

    • s/// jest używany standardowy sposób przeprowadzania podstawienia
    • ^\s* dopasowuje dowolne kolejne białe znaki na początku linii
    • \S+\s+ oznacza kolumnę danych (znaki inne niż białe znaki, a następnie znaki białe znaki)
    • (){4} oznacza, że ​​wzór powtarza się 4 razy.
  • sed and cut

    sed -r 's/^\s+//; s/\s+/\t/g' somefile | cut -f5-

    po prostu zastępując kolejne białe znaki jedną kartą;

  • tr and cut: trmożna również użyć do wyciskania kolejnych znaków z -sopcją.

    tr -s [:blank:] <somefile | cut -d' ' -f5-

-1

Przykłady awk wyglądają tutaj na skomplikowane, oto prosta składnia powłoki Bash:

command | while read -a cols; do echo ${cols[@]:1}; done

Gdzie 1jest Twoja n- ta kolumna, licząc od 0.


Przykład

Biorąc pod uwagę tę zawartość pliku ( in.txt):

c1
c1 c2
c1 c2 c3
c1 c2 c3 c4
c1 c2 c3 c4 c5

oto wynik:

$ while read -a cols; do echo ${cols[@]:1}; done < in.txt 

c2
c2 c3
c2 c3 c4
c2 c3 c4 c5

-1

Nie byłem zadowolony z żadnego z awkprzedstawionych tu rozwiązań, ponieważ chciałem wyodrębnić kilka pierwszych kolumn, a następnie wydrukować resztę, więc zwróciłem się do nich perl. Poniższy kod wyodrębnia dwie pierwsze kolumny i wyświetla pozostałe w niezmienionej postaci:

echo -e "a  b  c  d\te\t\tf g" | \
  perl -ne 'my @f = split /\s+/, $_, 3; printf "first: %s second: %s rest: %s", @f;'

Zaleta w porównaniu do perlrozwiązania Chrisa Koknata jest to, że tak naprawdę tylko pierwsze n elementów jest oddzielonych od ciągu wejściowego; reszta łańcucha nie jest w ogóle podzielona i dlatego pozostaje całkowicie nienaruszona. Mój przykład pokazuje to za pomocą kombinacji spacji i tabulatorów.

Aby zmienić liczbę kolumn, które należy wyodrębnić, zamień 3w tym przykładzie na n + 1.


-1
ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }'

z tej odpowiedzi nie jest źle, ale naturalne odstępy zniknęły.
Porównaj to z tym:

ls -la | cut -d\  -f4-

Wtedy zobaczysz różnicę.

Nawet ls -la | awk '{$1=$2=""; print}'która opiera się na odpowiedzi najlepszej do tej pory odpowiedzi, nie zachowuje formatowania.

W związku z tym użyłbym następujących, a także pozwala na wyraźne selektywne kolumny na początku:

ls -la | cut -d\  -f1,4-

Zauważ, że każda spacja również się liczy dla kolumn, więc na przykład poniżej kolumny 1 i 3 są puste, 2 to INFO, a 4 to:

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f1,3

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f2,4
INFO 2014-10-11
$

-1

Jeśli chcesz sformatowanego tekstu, połącz swoje polecenia za pomocą echa i użyj $ 0, aby wydrukować ostatnie pole.

Przykład:

for i in {8..11}; do
   s1="$i"
   s2="str$i"
   s3="str with spaces $i"
   echo -n "$s1 $s2" | awk '{printf "|%3d|%6s",$1,$2}'
   echo -en "$s3" | awk '{printf "|%-19s|\n", $0}'
done

Wydruki:

|  8|  str8|str with spaces 8  |
|  9|  str9|str with spaces 9  |
| 10| str10|str with spaces 10 |
| 11| str11|str with spaces 11 |

-9

Z powodu złej, najbardziej uprzywilejowanej odpowiedzi z 340 głosami, właśnie straciłem 5 minut życia! Czy ktoś wypróbował tę odpowiedź przed jej ponownym głosowaniem? Zupełnie nie. Kompletnie bezużyteczny.

Mam dziennik, w którym po 5 USD z adresem IP może być więcej tekstu lub brak tekstu. Potrzebuję wszystkiego, od adresu IP do końca linii, jeśli coś będzie po 5 USD. W moim przypadku jest to w rzeczywistości bez programu awk, a nie onkiner awk, więc awk musi rozwiązać problem. Kiedy próbuję usunąć pierwsze 4 pola przy użyciu najbardziej pozytywnej, ale całkowicie błędnej odpowiedzi:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'

wyrzuca złą i bezużyteczną odpowiedź (dodałem [..] w celu zademonstrowania):

[    37.244.182.218 one two three]

Istnieją nawet sugestie, aby połączyć substrat z tą błędną odpowiedzią. Podobnie jak komplikacja jest poprawą.

Zamiast tego, jeśli kolumny mają stałą szerokość do momentu, gdy potrzebny jest punkt cięcia i awk, prawidłowa odpowiedź to:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{printf "[%s]\n", substr($0,28)}'

co daje pożądaną wydajność:

[37.244.182.218 one two three]
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.