Zmiana kolejności kolumn za pomocą awk


13

Próbuję przenieść siódmą kolumnę mojego pliku csv do końca za pomocą

awk -F '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}',OFS= "$file"

gdzie $ file to plik .csv w katalogu. Jednak wynikiem jest

awk:                          ^ syntax error

Czy ktoś wie, jak naprawić ten błąd?


7
Pokazując błędy awk, musisz pokazać całą rzecz. ^Wskazuje część konkretnego polecenia gdzie napotkał błąd.
terdon

Odpowiedzi:


11

-FOpcja wymaga argumentu: -F,na przykład.

Koniec awkskryptu musi być oddzielony znakiem (spacją) z resztą parametrów.

Jeśli separatorem pól jest ,i chcesz go zachować, a liczba kolumn jest stała i mniejsza lub równa 11, spróbuj tego:

awk -F, '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' OFS=, "$file"

8
@anuribs na to pozwala bardzo niewiele programów. Standardowy sposób to command file > newfile && mv newfile file. Powiedział, że nowsza wersja GNU awkwspierać w ten sposób: gawk -i inplace '{blah blah}' file.
terdon

1
alternatywnie zamiast mv newfile fileciebie możesz użyć cat newfile > file ; rm -f newfile- to zachowuje i-węzeł i uprawnienia file.
cas

i generalnie dobrym pomysłem jest stosowanie mktempzamiast skryptów tymczasowych nazw plików w skryptach. np.tf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas

8

Byłoby krótsze rozwiązanie

awk -F',+' -v OFS=, '{$(NF+1)=$7; $7=""; $0=$0; $1=$1}1' file

Nie jestem pewien, czy ,+będzie działał we wszystkich awkwersjach, ale działa przynajmniej w GNU awk, także w -ctrybie zgodności.

Wyjaśnienie:

  • $(NF+1)=$7: najpierw dodajemy siódme pole na końcu linii (może być $12=$7w tym przypadku)
  • $7="": w następnym kroku 7. pole jest usuwane (ale otaczające ograniczniki pozostają)
  • aby usunąć ograniczniki, musimy ponownie ustawić cały rekord (przez $0=$0) traktując wiele przecinków jako separator pól (odbywa się to przez -F',+', tutaj +oznacza jeden lub więcej razy), a także zmienić kolejność bieżącego rekordu, $1=$1aby wymusić przebudowę linii za pomocą wcześniej ustawionego pola wyjściowego separator (ustawiany przez opcję -v OFS=,)
  • po zakończeniu tasowania jesteśmy gotowi wydrukować wynik 1

Przykładowe dane wejściowe:

1,2,3,4,5,6,7,8,9,10,11

wynik

1,2,3,4,5,6,8,9,10,11,7

Co jeśli inne kolumny są puste? Ale tak, FS jest wyrażeniem regularnym w POSIX (jeśli jest to wiele znaków), więc ,+powinno działać.
Random832

(1) Rozumiem, że spowodowanie „zniknięcia” siódmej kolumny danych wejściowych, a nie tylko ustawienie jej na zero, jest trudną częścią tego problemu. Ale, jak mówi Random832, twoje rozwiązanie blokuje puste kolumny (na przykład all,ball,call,,,fallall,ball,call,fall). (2)  $(NF+1)=$7to sprytne podejście. IMHO $0 = $0 OFS $7jest nieco jaśniejszy, ma tylko kilka znaków dłużej i wydaje się, że robi to samo. Czy możesz pomyśleć o sytuacji, w której $0 = $0 OFS $7kod nie działa tak samo jak kod?
G-Man mówi „Przywróć Monikę”

@ Random832 @ G-Man tak, niektóre przypadki krawędzi, takie jak puste pola, puste linie lub NF <7 powinny być traktowane osobno lub należy zmienić kolejność kodu. To tylko pomysł, a nie „kompletne rozwiązanie” dla wszystkich ogólnych przypadków, które powinno być jasne. $0=$0 OFS $7jest prawdopodobnie identyczny $(NF+1)=$7, ale tylko z resztą kodu niezmienioną, ogólnie nie.
jimmij

5

Jeśli drukujesz za pomocą OFS=, więc bez separatora między polami, możesz po prostu zapisać wartość $7zmiennej, ustawić $7puste i wydrukować linię i zmienną bezpośrednio. Nie musisz określać wszystkich pól:

$ cat file
1,2,3,4,5,6,7,8
$ awk -F, -vOFS= '{k=$7; $7=""; print $0,k}' file 
12345687


3

Nie specjalnie powiedzieć, że chciał użyć awk, a nie mówią, że chciał użyć edycji w miejscu jak zapewnia sed -i, więc o to sed -iwariant. Zwykle awklepiej jest pracować z kolumnami, ale jest to jeden przypadek, w którym wolę sed, ponieważ naturalnie obsługuje dowolną liczbę kolumn.

MOVECOL=7
N=$((MOVECOL-1))
sed -r -e "s/^(([^,]*,){$N})([^,]*),(.*)/\1\4,\3/" -i test.csv

Wyjaśnienie:

  • -r wybiera rozszerzone wyrażenia regularne, aby uniknąć mnożenia odwrotnych ukośników
  • pierwsza grupa to $ N powtórzeń ciągów zakończonych przecinkami, innymi słowy kolumny przed tą, którą chcemy przenieść, z końcowym przecinkiem
  • druga grupa to N-ta powtórzenie, o której zapominamy
  • trzecia grupa to kolumna, którą chcemy przenieść, bez końcowego przecinka
  • czwarta grupa składa się ze wszystkich kolumn po tej, którą chcemy przenieść, bez przecinka wcześniej
  • zastępujemy pierwszą grupą, ostatnią grupą i wyodrębnioną kolumną, w razie potrzeby wstawiając przecinek.

Oczywiście nie będzie to działać z plikami, które ukrywają przecinki w cudzysłowach (lub, co gorsza, unikaj ich), ale awk nie poradzi sobie z tym bez poważnych działań akrobatycznych. Jeśli masz ten problem, lepiej skorzystaj z perlmodułu Text:CSVlub pythonmodułu csv.


2

Kilka awkwariantów (zakładając, że plik znajduje się w zmiennej $file)

  • Tutaj możesz przejechać cały kolor, wydrukować za pomocą separatora pól (OFS) i wydrukować terminator rekordów (ORS) na końcu wiersza.

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • Tutaj z użyciem wyrażenia regularnego i gensub()funkcji

    gawk -F',+' -v OFS=, '{$0=gensub(/\s*\S+/,"",7) OFS $7}1' "$file"

    zabijanie na 7 th pola i drukowanie go na końcu linii.

    • $0 to cały rekord
    • $njest n- tym rekordem
    • NF to liczba pól bieżącej linii
    • OFS separator wyjściowy złożony
    • ORS terminator rekordu wyjściowego
    • 1to sztuczka, by powiedzieć awk truei wydrukować default ( $0).

Zaktualizuj ...

Prawie zapominam, że możliwe jest przesunięcie wszystkich kolumn po siódmej .

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"

(1) Prawdopodobnie OFS $7byłby bardziej solidny niż "," $7. (2) Uważam, że ", " $7jest to złe, o ile pytanie wskazuje, że OP nie chce spacji po przecinkach. (A jeśli dane wejściowe miałyby spacje po przecinkach, wtedy $7już zaczynałyby się spacją, a ty dodawalibyśmy dodatkową.)
G-Man mówi „Przywróć Monikę”

@ G-Man To było głównie zaproponowanie pewnych pomysłów, niektórych wariantów. Dzięki, za miejsce, zgadzam się co do tego OFS $7, że nie tylko bardziej solidny, ale nawet bardziej ogólny ( „pośpiech czyni marnotrawstwo” )
Hastur
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.