@Jefromi - również cutnie ma wyrażeń regularnych przed {}akcjami, a potem jest o wiele głupiej z ogranicznikami pól (zmienna liczba spacji?) I musisz je określić ręcznie. Myślę, że OP chciał usłyszeć o jakiejś shift Nkomendzie, której nie ma. Najbliższy jest $1="";$2="";(...);print}, ale w moim przypadku pozostawia kilka wiodących spacji (prawdopodobnie separatory).
Tomasz Gandor
Odpowiedzi:
50
Rozwiązanie, które nie dodaje dodatkowych początkowych ani końcowych spacji :
Odpowiedź EdMortona nie zadziałała dla mnie (bash 4.1.2 (1) -release, GNU Awk 3.1.7 lub bash 3.2.25 (1) -release, GNU Awk 3.1.5), ale znalazłam tutaj inny sposób:echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
elysch
1
@elysch nie, to ogólnie nie zadziała, po prostu wydaje się działać przy pewnych określonych wartościach wejściowych. Zobacz komentarz, który dodałem poniżej twojego komentarza pod moją odpowiedzią.
Ed Morton,
1
Cześć @fedorqui. Moja odpowiedź jest pierwsza. W mojej pierwotnej odpowiedzi wyjaśniałem, dlaczego druga odpowiedź nie jest poprawna (dodatkowe początkowe lub końcowe spacje). Niektórzy ludzie zaproponowali ulepszenia w komentarzach. Poprosiliśmy OP, aby wybrał bardziej poprawną odpowiedź, a on / ona wybrał moją. Po tym, jak kilku innych współtwórców zredagowało moją odpowiedź, aby odwołać się do odpowiedzi (zobacz historię). Czy to dla ciebie jasne? Co radzisz mi, aby poprawić zrozumiałość mojej odpowiedzi? Pozdrawiam ;-)
olibre
1
Masz całkowitą rację i bardzo mi przykro z powodu mojego nieporozumienia. Szybko przeczytałem odpowiedź i nie zauważyłem Twojej oryginalnej odpowiedzi (tak, czytałem za szybko). +1 dla samej odpowiedzi, używając fajnej sztuczki, aby zapętlić się do NF-1, a następnie wypisać ostatni element, aby uniknąć dodatkowych białych znaków. I znowu przepraszam! (usuniemy mój komentarz za dzień lub dwa, aby zapobiec nieporozumieniom ze strony przyszłych czytelników).
fedorqui 'SO przestań szkodzić'
1
Użyłbym jakiegoś rodzaju nagłówków: <twoja odpowiedź>, a następnie pozioma reguła, po której następuje duży tytuł „porównanie innych odpowiedzi”. W przeciwnym razie przenieś to porównanie do innej odpowiedzi, ponieważ najwyraźniej ludzie wolą krótkie odpowiedzi w wizji „daj mi mój kod”
prawdopodobnie lepiej jest użyć „NF” niż „13” w ostatnim przykładzie.
glenn jackman
2
2 scenariusz, o którym decyduje OP. jeśli 13 jest ostatnim polem, użycie NF jest w porządku. Jeśli nie, użycie 13 jest właściwe.
ghostdog74
3
2. musi usunąć 3 kopie OFS od początku $ 0. Trzeci byłby lepszy z printf "%s ",$i, ponieważ nie wiesz, czy $imoże zawierać %slub tym podobne. Ale to spowodowałoby wydrukowanie dodatkowej spacji na końcu.
To miłe ze względu na jego dynamikę. Możesz dodać kolumny na końcu i nie przepisywać swoich skryptów.
MinceMan
1
To pokazuje dokładnie, jaki problem ma rozwiązać pytanie, po prostu zrób coś odwrotnego. A co z wydrukowaniem z setnego pola? Zauważ, że nie masz do czynienia, NFwięc odchodzisz na prowadzenie OFS.
Chris Seymour,
24
Prawidłowym sposobem na to jest zastosowanie interwału RE, ponieważ pozwala on po prostu określić, ile pól ma zostać pominiętych, i zachowuje odstępy między polami dla pozostałych pól.
np. aby pominąć pierwsze 3 pola bez wpływu na odstępy między pozostałymi polami, biorąc pod uwagę format danych wejściowych, który wydaje się omawiać w tym pytaniu, jest po prostu:
Jeśli masz FS będący RE, którego nie możesz zanegować w zestawie znaków, możesz najpierw przekonwertować go na pojedynczy znak (RS jest idealne, jeśli jest to pojedynczy znak, ponieważ RS NIE MOŻE pojawić się w polu, w przeciwnym razie rozważ SUBSEP), następnie zastosuj podstawienie interwału RE, a następnie przekonwertuj na OFS. np. jeśli łańcuchy znaków „.” rozdzielają pola:
Wtedy masz ten sam problem, co w przypadku wszystkich rozwiązań opartych na pętli, które ponownie przypisują pola - FS są konwertowane na OFS. Jeśli to jest problem, musisz przyjrzeć się funkcji patsplit () w GNU awks.
Nie działało dla mnie (bash 4.1.2 (1) -release, GNU Awk 3.1.7 lub bash 3.2.25 (1) -release, GNU Awk 3.1.5), ale znalazłem tutaj inny sposób:echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
elysch
2
Nie, to się nie powiedzie, jeśli $ 1 lub $ 2 zawiera ciąg, na który ustawiono $ 3. Spróbuj, na przykład, echo ' That is a test' | awk '{print substr($0, index($0,$3))}'a przekonasz się, aże to, co jest 3 $, odpowiada awewnętrznemu That1 $. W bardzo starej wersji gawk, takiej jak ty, musisz włączyć interwały RE za pomocą flagi --re-interval.
Ed Morton,
2
Masz rację, nie zauważyłem. Przy okazji, naprawdę doceniam twój komentarz. Wiele razy chciałem użyć wyrażenia regularnego z „{}” do określenia liczby elementów i nigdy nie widziałem „--re-interval” w man. +1 dla Ciebie.
wiem, jakie to kanoniczne, ale dodałem teraz odpowiedź.
Ed Morton
10
Prawie wszystkie odpowiedzi obecnie dodają spacje wiodące, spacje końcowe lub inny problem z separatorem. Aby wybrać z czwartego pola, w którym separatorem jest biały znak, a separatorem wyjściowym jest pojedyncza spacja, użyj awk:
Lub aby umieścić je w tej samej linii, przypisz 3 $ do 1 $ itd., A następnie zmień NF na odpowiednią liczbę pól. echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) $(i-2)=$i; NF=NF-2; print $0 }'
larsr
Cześć @larsr. Twoja proponowana linia poleceń to jedyna poprawna odpowiedź. Wszystkie inne odpowiedzi dodają dodatkowe spacje (początkowe lub końcowe). Wpisz swoją linię poleceń w nowej odpowiedzi, zagłosuję za nią ;-)
olibre
1
Cześć @sudo_O, rozmawiałem z @larsr na temat wiersza poleceń, które zaproponował w swoim komentarzu. Spędziłem około pięciu minut, zanim zrozumiałem quiproco (nieporozumienie). Zgadzam się, odpowiedź @Vetsin wstawia nowe linie ( ORS) między polami. Brawo za twoją inicjatywę (podoba mi się twoja odpowiedź). Pozdrawiam
olibre
3
Inny sposób uniknięcia używania instrukcji print:
$ awk '{$1=$2=$3=""}sub("^"FS"*","")' file
W awk, gdy warunek jest prawdziwy, domyślną akcją jest print.
+1 za podobne rozwiązanie ... Ale może to mieć problemy z wydajnością, jeśli filejest duże (> 10-30 KB). W przypadku dużych plików awkrozwiązanie działa lepiej.
TrueY
3
Opcje od 1 do 3 mają problemy z wieloma białymi znakami (ale są proste). Z tego powodu opracowano opcje 4 i 5, które bez problemu przetwarzają wiele białych znaków. Oczywiście, jeśli opcje 4 lub 5 są używane z n=0obiema, zachowa wszelkie wiodące białe spacje, co n=0oznacza brak podziału.
opcja 1
Proste rozwiązanie cięcia (działa z pojedynczymi ogranicznikami):
$ echo '1 2 3 4 5 6 7 8'| cut -d' '-f4-45678
Opcja 2
Wymuszenie ponownego obliczenia awk czasami rozwiązuje problem (działa z niektórymi wersjami awk) dodanych spacji wiodących:
UWAGA: "^ [" FS "] *" ma akceptować dane wejściowe ze spacjami wiodącymi.
Opcja 5
Całkiem możliwe jest zbudowanie rozwiązania, które nie dodaje dodatkowych początkowych ani końcowych białych znaków i zachowuje istniejące białe znaki przy użyciu funkcji gensubz GNU awk, jak to:
Cześć BZ. Twoja odpowiedź jest miła. Ale opcja 3 nie działa na łańcuchu rozpoczynającym się spacją (np " 1 2 3 4 5 6 7 8 ".). Opcja 4 jest fajna, ale zostaw wiodącą spację za pomocą łańcucha rozpoczynającego się spacją. Myślisz, że można to naprawić? Możesz użyć polecenia echo " 1 2 3 4 5 6 7 8 " | your awk script | sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/', aby zweryfikować spacje
początkowe
Cześć @olibre. To, że opcja 3 kończy się niepowodzeniem ze spacją, jest powodem rozwinięcia opcji 4 i 5. Opcja 4 pozostawia spację początkową tylko wtedy, gdy na wejściu ją występuje, a n jest ustawione na 0 (n = 0). Uważam, że jest to poprawna odpowiedź, gdy nie ma wyboru pól (nic nie naprawia IMO). Twoje zdrowie.
W porządku. Dzięki za dodatkowe informacje :-) Popraw swoją odpowiedź, podając te dodatkowe informacje :-) Pozdrawiam
olibre
Idealnie :-) Szkoda, że twój użytkownik jest wyłączony :-(
olibre
1
Cut ma flagę --complement, która ułatwia (i przyspiesza) usuwanie kolumn. Wynikowa składnia jest analogiczna do tego, co chcesz zrobić - dzięki czemu rozwiązanie jest łatwiejsze do odczytania / zrozumienia. Uzupełnienie działa również w przypadku, gdy chcesz usunąć nieciągłe kolumny.
Czy powyższa zmiana pomaga w zrozumieniu? Chodzi o to, aby użyć flagi uzupełnienia cięcia. Rozwiązanie powinno być szybszą i bardziej zwięzłą implementacją niż rozwiązania oparte na AWK lub Perlu. Można również wycinać dowolne kolumny.
Michael Back
1
Rozwiązanie Perla, które nie dodaje początkowych ani końcowych spacji:
Ponieważ byłem zirytowany pierwszą bardzo pozytywną, ale złą odpowiedzią, znalazłem wystarczająco dużo, aby napisać tam odpowiedź, a tutaj złe odpowiedzi są oznaczone jako takie, oto mój kawałek. Nie podobają mi się proponowane rozwiązania, ponieważ nie widzę powodu, aby komplikować odpowiedź.
Mam dziennik, w którym po 5 $ z adresem IP może być więcej tekstu lub brak tekstu. Potrzebuję wszystkiego, od adresu IP do końca linii, jeśli po 5 $ będzie coś. W moim przypadku jest to faktycznie program awk, a nie oneliner awk, więc awk musi rozwiązać problem. Kiedy próbuję usunąć pierwsze 4 pola, używając starej, ładnie wyglądającej i najbardziej pozytywnej, ale całkowicie błędnej odpowiedzi:
echo " 7 27.10.16. Thu 11:57:18 37.244.182.218 one two three"| awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'
wypluwa złą i bezużyteczną odpowiedź (dodałem [], aby zademonstrować):
[37.244.182.218 one two three]
Zamiast tego, jeśli kolumny mają stałą szerokość aż do punktu cięcia i awk, poprawna i dość prosta odpowiedź brzmi:
echo " 7 27.10.16. Thu 11:57:18 37.244.182.218 one two three"| awk '{printf "[%s]\n", substr($0,28)}'
W %-5swyrównuje wynik jako 5-znakowego szerokich kolumn; jeśli to nie wystarczy, zwiększ liczbę lub użyj %s(ze spacją), jeśli nie zależy Ci na wyrównaniu.
Rozwiązanie oparte na AWK printf, które pozwala uniknąć% problemu i jest wyjątkowe, ponieważ nie zwraca niczego (bez znaku powrotu), jeśli jest mniej niż 4 kolumny do wydrukowania:
cut -f3-
?cut
nie ma wyrażeń regularnych przed{}
akcjami, a potem jest o wiele głupiej z ogranicznikami pól (zmienna liczba spacji?) I musisz je określić ręcznie. Myślę, że OP chciał usłyszeć o jakiejśshift N
komendzie, której nie ma. Najbliższy jest$1="";$2="";(...);print}
, ale w moim przypadku pozostawia kilka wiodących spacji (prawdopodobnie separatory).Odpowiedzi:
Rozwiązanie, które nie dodaje dodatkowych początkowych ani końcowych spacji :
Sudo_O proponuje eleganckie ulepszenie przy użyciu operatora trójskładnikowego
NF?ORS:OFS
EdMorton zapewnia rozwiązanie zachowujące oryginalne spacje między polami:
BinaryZebra zapewnia również dwa niesamowite rozwiązania:
(te rozwiązania zachowują nawet końcowe spacje z oryginalnego ciągu)
Rozwiązanie podane przez larsra w komentarzach jest prawie poprawne:
Oto stała i sparametryzowana wersja rozwiązania larsr :
Wszystkie inne odpowiedzi przed wrz 2013 są miłe, ale dodaj dodatkowe spacje:
Przykład odpowiedzi dodającej dodatkowe spacje wiodące :
Przykład odpowiedzi dodającej dodatkową spację na końcu
źródło
echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
źródło
OFS
ponieważ nie zajmujesz sięNF
np. Wiodącą spacją w rekordach.użyj cięcia
lub jeśli nalegasz na awk i 13 $ jest ostatnim polem
jeszcze
źródło
printf "%s ",$i
, ponieważ nie wiesz, czy$i
może zawierać%s
lub tym podobne. Ale to spowodowałoby wydrukowanie dodatkowej spacji na końcu.Spróbuj tego:
źródło
NF
więc odchodzisz na prowadzenieOFS
.Prawidłowym sposobem na to jest zastosowanie interwału RE, ponieważ pozwala on po prostu określić, ile pól ma zostać pominiętych, i zachowuje odstępy między polami dla pozostałych pól.
np. aby pominąć pierwsze 3 pola bez wpływu na odstępy między pozostałymi polami, biorąc pod uwagę format danych wejściowych, który wydaje się omawiać w tym pytaniu, jest po prostu:
Jeśli chcesz uwzględnić spacje wiodące i niepuste, ale znowu z domyślnym FS, to jest to:
Jeśli masz FS będący RE, którego nie możesz zanegować w zestawie znaków, możesz najpierw przekonwertować go na pojedynczy znak (RS jest idealne, jeśli jest to pojedynczy znak, ponieważ RS NIE MOŻE pojawić się w polu, w przeciwnym razie rozważ SUBSEP), następnie zastosuj podstawienie interwału RE, a następnie przekonwertuj na OFS. np. jeśli łańcuchy znaków „.” rozdzielają pola:
Oczywiście, jeśli OFS jest pojedynczym znakiem ORAZ nie może pojawić się w polach wejściowych, możesz to zredukować do:
Wtedy masz ten sam problem, co w przypadku wszystkich rozwiązań opartych na pętli, które ponownie przypisują pola - FS są konwertowane na OFS. Jeśli to jest problem, musisz przyjrzeć się funkcji patsplit () w GNU awks.
źródło
echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
echo ' That is a test' | awk '{print substr($0, index($0,$3))}'
a przekonasz się,a
że to, co jest 3 $, odpowiadaa
wewnętrznemuThat
1 $. W bardzo starej wersji gawk, takiej jak ty, musisz włączyć interwały RE za pomocą flagi--re-interval
.1
jest spełnionym warunkiem, więc wywołuje domyślną akcję awk wypisywania bieżącego rekordu.Prawie wszystkie odpowiedzi obecnie dodają spacje wiodące, spacje końcowe lub inny problem z separatorem. Aby wybrać z czwartego pola, w którym separatorem jest biały znak, a separatorem wyjściowym jest pojedyncza spacja, użyj
awk
:Aby sparametryzować pole początkowe, możesz zrobić:
A także pole końcowe:
źródło
Wejście
Wynik
źródło
źródło
echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) $(i-2)=$i; NF=NF-2; print $0 }'
ORS
) między polami. Brawo za twoją inicjatywę (podoba mi się twoja odpowiedź). PozdrawiamInny sposób uniknięcia używania instrukcji print:
W awk, gdy warunek jest prawdziwy, domyślną akcją jest print.
źródło
awk '{$1=$2=$3=""}sub("^"OFS"+","")' file
podobnie jak OFS, co pozostaje po zmianie zawartości 1 $, 2 $ i 3 $.Nie mogę uwierzyć, że nikt nie zaoferował zwykłej powłoki:
źródło
file
jest duże (> 10-30 KB). W przypadku dużych plikówawk
rozwiązanie działa lepiej.Opcje od 1 do 3 mają problemy z wieloma białymi znakami (ale są proste). Z tego powodu opracowano opcje 4 i 5, które bez problemu przetwarzają wiele białych znaków. Oczywiście, jeśli opcje 4 lub 5 są używane z
n=0
obiema, zachowa wszelkie wiodące białe spacje, con=0
oznacza brak podziału.opcja 1
Proste rozwiązanie cięcia (działa z pojedynczymi ogranicznikami):
Opcja 2
Wymuszenie ponownego obliczenia awk czasami rozwiązuje problem (działa z niektórymi wersjami awk) dodanych spacji wiodących:
Opcja 3
Wydrukowanie każdego pola sformatowanego za pomocą
printf
da większą kontrolę:Jednak wszystkie poprzednie odpowiedzi zmieniają wszystkie FS między polami na OFS. Zbudujmy kilka rozwiązań tego problemu.
Opcja 4
Pętla z sub do usuwania pól i ograniczników jest bardziej przenośna i nie powoduje zmiany FS na OFS:
UWAGA: "^ [" FS "] *" ma akceptować dane wejściowe ze spacjami wiodącymi.
Opcja 5
Całkiem możliwe jest zbudowanie rozwiązania, które nie dodaje dodatkowych początkowych ani końcowych białych znaków i zachowuje istniejące białe znaki przy użyciu funkcji
gensub
z GNU awk, jak to:Można go również użyć do zamiany listy pól z liczbą
n
:Oczywiście w takim przypadku OFS jest używany do oddzielenia obu części wiersza, a końcowe białe znaki pól są nadal drukowane.
Uwaga 1:
["FS"]*
służy do dopuszczania spacji wiodących w wierszu wejściowym.źródło
" 1 2 3 4 5 6 7 8 "
.). Opcja 4 jest fajna, ale zostaw wiodącą spację za pomocą łańcucha rozpoczynającego się spacją. Myślisz, że można to naprawić? Możesz użyć poleceniaecho " 1 2 3 4 5 6 7 8 " | your awk script | sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
, aby zweryfikować spacjeCut ma flagę --complement, która ułatwia (i przyspiesza) usuwanie kolumn. Wynikowa składnia jest analogiczna do tego, co chcesz zrobić - dzięki czemu rozwiązanie jest łatwiejsze do odczytania / zrozumienia. Uzupełnienie działa również w przypadku, gdy chcesz usunąć nieciągłe kolumny.
źródło
Rozwiązanie Perla, które nie dodaje początkowych ani końcowych spacji:
Perl
@F
Tablica autosplit zaczyna się od indeksu,0
podczas gdy pola awk zaczynają się od$1
Rozwiązanie Perla dla danych rozdzielanych przecinkami:
Rozwiązanie w Pythonie:
python -c "import sys;[sys.stdout.write(' '.join(line.split()[3:]) + '\n') for line in sys.stdin]" < file
źródło
Dla mnie najbardziej kompaktowym i zgodnym rozwiązaniem na żądanie jest
A jeśli masz więcej wierszy do przetworzenia, jak na przykład plik foo.txt , nie zapomnij zresetować i do 0:
Dzięki za forum.
źródło
Ponieważ byłem zirytowany pierwszą bardzo pozytywną, ale złą odpowiedzią, znalazłem wystarczająco dużo, aby napisać tam odpowiedź, a tutaj złe odpowiedzi są oznaczone jako takie, oto mój kawałek. Nie podobają mi się proponowane rozwiązania, ponieważ nie widzę powodu, aby komplikować odpowiedź.
Mam dziennik, w którym po 5 $ z adresem IP może być więcej tekstu lub brak tekstu. Potrzebuję wszystkiego, od adresu IP do końca linii, jeśli po 5 $ będzie coś. W moim przypadku jest to faktycznie program awk, a nie oneliner awk, więc awk musi rozwiązać problem. Kiedy próbuję usunąć pierwsze 4 pola, używając starej, ładnie wyglądającej i najbardziej pozytywnej, ale całkowicie błędnej odpowiedzi:
wypluwa złą i bezużyteczną odpowiedź (dodałem [], aby zademonstrować):
Zamiast tego, jeśli kolumny mają stałą szerokość aż do punktu cięcia i awk, poprawna i dość prosta odpowiedź brzmi:
który daje pożądaną wydajność:
źródło
Znalazłem inną możliwość, może przyda się też ...
awk 'BEGIN {OFS=ORS="\t" }; {for(i=1; i<14; i++) print $i " "; print $NF "\n" }' your_file
Uwaga: 1. Dla danych tabelarycznych iz kolumny $ 1 do $ 14
źródło
Użyj cięcia:
np .: jeśli masz
file1
:car.is.nice.equal.bmw
Uruchom:
cut -d . -f1,3 file1
wydrukujecar.is.nice
źródło
To nie jest dalekie od niektórych poprzednich odpowiedzi, ale rozwiązuje kilka problemów:
cols.sh
:Które możesz teraz wywołać z argumentem, który będzie kolumną początkową:
Lub:
To jest indeksowane 1; jeśli wolisz indeksowane zero, użyj
i=s + 1
zamiast tego.Ponadto, jeśli chcesz mieć argumenty za indeksem początkowym i końcowym, zmień plik na:
Na przykład:
W
%-5s
wyrównuje wynik jako 5-znakowego szerokich kolumn; jeśli to nie wystarczy, zwiększ liczbę lub użyj%s
(ze spacją), jeśli nie zależy Ci na wyrównaniu.źródło
Rozwiązanie oparte na AWK printf, które pozwala uniknąć% problemu i jest wyjątkowe, ponieważ nie zwraca niczego (bez znaku powrotu), jeśli jest mniej niż 4 kolumny do wydrukowania:
Testowanie:
źródło