Jak usunąć ostatnią kolumnę pliku w systemie Linux

25

Chcę usunąć ostatnią kolumnę pliku txt, a nie wiem, jaki jest numer kolumny. Jak mogłem to zrobić?

Przykład:

Wkład:

1223 1234 1323 ... 2222 123
1233 1234 1233 ... 3444 125
0000 5553 3455 ... 2334 222

Chcę, aby mój wynik był:

1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334
zara
źródło
Można to zrobić na wiele sposobów. Dodaj przykład i spodziewane wyniki.
heemayl,
@ heemayl ok zrobiłem
Zara
Dzięki .. czy karta kolumny jest oddzielona czy spacja?
heemayl
@heemayl space is deliminator
zara

Odpowiedzi:

43

Z awk:

awk 'NF{NF-=1};1' <in >out

lub:

awk 'NF{NF--};1' <in >out

lub:

awk 'NF{--NF};1' <in >out

Chociaż wygląda to jak voodoo, działa. Każda z tych komend awk składa się z trzech części.

Pierwsza to NFwarunek wstępny drugiej części. NFjest zmienną zawierającą liczbę pól w linii. W AWK rzeczy są prawdziwe, jeśli nie są 0 lub pusty ciąg "". Dlatego druga część (gdzie NFjest zmniejszana) dzieje się tylko wtedy, gdy NFnie jest równa 0.

Druga część (albo NF-=1 NF--albo --NF) odejmuje jedną od NFzmiennej. Zapobiega to drukowaniu ostatniego pola, ponieważ podczas zmiany pola (w tym przypadku usuwając ostatnie pole), awkponownie konstruuj $0, łącz wszystkie domyślnie pola oddzielone spacją. $0nie zawierał już ostatniego pola.

Ostatnia część to 1. To nie jest magiczne, jest po prostu używane jako wyrażenie, które oznacza true. Jeśli awkwyrażenie ma wartość true bez powiązanej akcji, awkdomyślną akcją jest print $0.

Cuonglm
źródło
@JJoao: Ach, dziękuję, zapomniałem --. Uwaga: obecnie potrzebujesz ;1zgodności z POSIX.
cuonglm,
Moim początkowym instynktem byłoby użycie pętli for, ale jest to o wiele bardziej zwięzłe i sprytne.
Sergiy Kolodyazhnyy
5
Warto zauważyć, że jeśli używasz domyślnego ogranicznika, musisz wprowadzić pewne zmiany. Zakładając, że ,Twoim ogranicznikiem jest:awk -F',' 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' < in > out
Pan Lama,
1
Efektem zmniejszenia wartości NF jest niezdefiniowane zachowanie POSIX - otrzymasz różne dane wyjściowe w zależności od uruchomionego awk. Niektóre awki usuwają ostatnie pole, jak chcesz, niektóre w ogóle nic nie robią, a inne mogą zgłaszać błąd składniowy lub cokolwiek innego.
Ed Morton
16

Korzystanie grepz PCRE:

$ grep -Po '.*(?=\s+[^\s]+$)' file.txt 
1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334

Za pomocą GNU sed:

$ sed -r 's/(.*)\s+[^\s]+$/\1/' file.txt 
1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334
heemayl
źródło
1
@ramin Sure .. czy możesz zadać to pytanie jako nowe pytanie (tak działa ta strona) :)
heemayl
@ramin Czy daje jakieś ograniczenia czasowe lub jakieś ostrzeżenie?
heemayl,
mówi, że to nie jest standardowe pytanie!
zara,
@ Ramin Ok. skontaktuj się z administratorem, może oni mogą ci w tym pomóc .. czy sprawdziliście już jakąś starą kontrolę jakości dotyczącą twojego pytania? istnieje możliwość, że pytanie zostało już zadane i udzielono odpowiedzi.
heemayl,
3
Nie zadawaj bardzo podstawowych pytań, takich jak „ jak zmienić nazwę nazwy pliku w systemie Linux ”. Użyj Google.
Christoffer Hammarström,
11

Za pomocą Perla:

perl -lane '$,=" ";pop(@F);print(@F)' in

Używanie rev+ cut:

rev in | cut -d ' ' -f 2- | rev
kos
źródło
5

Za pomocą GNU sed:

sed -r 's/\s+\S+$//' input.txt

Mówiąc bardziej ogólnie, ten działa z sedem BSD w OSX, a także GNU sed:

sed 's/[[:space:]]\{1,\}[^[:space:]]\{1,\}$//' input.txt
Cyfrowa trauma
źródło
1

Jeśli separator jest zawsze pojedynczym znakiem (więc dwa lub więcej kolejnych separatorów oznacza puste pola), możesz headpo prostu pierwszy wiersz z pliku wejściowego, policzyć separatory ( nseparatory oznaczają liczbę pól n+1), a następnie użyć cutdo wydrukowania z 1pola st do npola th (od drugiego do ostatniego), np. z wprowadzeniem rozdzielanym tabulatorami:

n=$(head -n 1 infile | tr -dc \\t | tr \\t \\n | wc -l)
cut -f1-$n infile > outfile

lub np. z plikiem csv :

n=$(head -n 1 infile | tr -dc , | tr , \\n | wc -l)
cut -d, -f1-$n infile > outfile

Uruchomię kilka testów porównawczych później, jeśli będę miał czas, ale przy dużym wkładzie myślę, że to rozwiązanie powinno być szybsze niż inne rozwiązania, które używają wyrażenia regularnego, ponieważ to robi minimalne przetwarzanie w pierwszej linii, aby uzyskać nie. pól, a następnie zastosowań cutzoptymalizowanych do tego zadania.

don_crissti
źródło
1

Przenośnie możesz użyć jednego z tych:

sed 's/[[:space:]]*[^[:space:]]*$//' file

awk '{sub(/[[:space:]]*[^[:space:]]*$/,"")}1' file
Ed Morton
źródło
0

Korzystanie z vima:

Otwórz plik w vimie

vim <filename> 

Przejdź do pierwszego rzędu, na wypadek, gdyby kursor został umieszczony gdzie indziej.

gg

Utwórz makro o nazwie „q” qq, które przechodzi na koniec bieżącej linii $, a następnie wraca do ostatniej spacji F( duża litera F, a następnie dosłowna SPACJA), następnie usuwa z bieżącej pozycji do końca linii i Dprzechodzi do następnej linii ji zatrzymać nagrywanie makra za pomocą q.

qq$F Djq

Teraz możemy powtórzyć nasze makro @qdla każdego wiersza.
Możemy również nacisnąć, @@aby powtórzyć ostatnie makro lub nawet łatwiej:

99@q

powtórzyć makro 99 razy.
Uwaga: Liczba nie może dokładnie odpowiadać wierszom.

cee
źródło
0

W przypadku osób, które mają podobny problem, ale z różnymi separatorami pól, ta awkmetoda zachowa poprawnie separator pól:

$ cat file 
foo.bar.baz
baz.bar.foo
$ awk -F'.' 'sub(FS $NF,x)' file
foo.bar
baz.bar
htaccess
źródło