Jak mogę wykonać następujące czynności dla pliku CSV za pomocą sed
lub awk
?
- Usuń kolumnę
- Duplikuj kolumnę
- Przenieś kolumnę
Mam duży stół z ponad 200 rzędami i nie znam się tak dobrze sed
.
Jak mogę wykonać następujące czynności dla pliku CSV za pomocą sed
lub awk
?
Mam duży stół z ponad 200 rzędami i nie znam się tak dobrze sed
.
Odpowiedzi:
Oprócz tego, jak wycinać i ponownie układać pola (omówione w innych odpowiedziach), istnieje problem dziwnych pól CSV.
Jeśli Twoje dane należą do tej „dziwacznej” kategorii, możesz zająć się tym trochę wstępnego i końcowego filtrowania. Filtry przedstawione poniżej wymagają znaki \x01
, \x02
, \x03
, \x04
aby nie pojawia się nigdzie w swoich danych.
Oto filtry owinięte wokół prostego awk
zrzutu pola.
Uwaga: pole pięć ma niepoprawny / niekompletny układ „pola cytowanego”, ale jest łagodny na końcu wiersza (w zależności od parsera CSV). Ale, oczywiście, spowodowałoby to problematyczne nieużywane wyniki, gdyby miał zostać zamieniony z bieżącej pozycji na końcu rzędu .
Aktualizacja; użytkownik121196 wskazał błąd, gdy przecinek poprzedza końcowy cytat. Oto poprawka.
Dane
cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF
Kod
sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g'
Wyjście:
field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five
"15111 N. Hayden Rd., Ste 160,"
""
Oto filtr wstępny , rozszerzony o komentarze. Filtr po to tylko odwrócenie . , ,\x01
\x02
\x03
\x04
sed -r '
s/^/,/ # add a leading comma delimiter
s/\\"/\x01/g # obfuscate escaped quotation-mark (\")
s/,"([^"]*)"/,\x02\1\x03/g # obfuscate quotation-marks
s/,"/,\x02/ # when no trailing quote on last field
:MC # obfuscate commas embedded in quotes
s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
tMC
s/^,// # remove spurious leading delimiter
'
Zależy to od tego, czy plik CSV używa przecinków tylko do ograniczników, czy też masz szaleństwo, takie jak:
pole pierwsze, „pole dwa”, pole trzecie
Zakłada się, że używasz prostego pliku CSV:
Możesz pozbyć się jednej kolumny na wiele sposobów; Jako przykład użyłem kolumny 2. Najłatwiejszym sposobem jest prawdopodobnie użycie cut
, które pozwala określić ogranicznik -d
i które pola chcesz wydrukować -f
; nakazuje to podział na przecinki i pole wyjściowe 1, a pola 3 do końca:
$ cut -d, -f1,3- /path/to/your/file
Jeśli faktycznie potrzebujesz użyć sed
, możesz napisać wyrażenie regularne pasujące do pierwszych n-1
pól, pola n
th i reszty, i pomiń wypisywanie n
th (tutaj n
jest 2, więc pierwsza grupa dopasowuje 1
czas :) \{1\}
:
$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file
Istnieje wiele sposobów, aby to zrobić awk
, żaden z nich nie jest szczególnie elegancki. Możesz użyć for
pętli, ale radzenie sobie z przecinkiem końcowym jest uciążliwe; ignorując, że byłoby to coś takiego:
$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file
Uważam, że łatwiej jest wyprowadzić pole 1, a następnie użyć substr
do wyciągnięcia wszystkiego po polu 2:
$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file
Jest to denerwujące dla kolumn dalej
W sed
ten jest zasadniczo taki sam wyraz twarzy jak poprzednio, ale także przechwytywać kolumnę docelową i uwzględnić tę grupę wielokrotnie w wymianie:
$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file
W awk
przypadku pętli for byłoby to coś w stylu (ponownie ignorując przecinek końcowy):
$ awk -F, '{
for(i=1; i<=NF; i++) {
if(i == 2) printf "%s,", $i;
printf "%s,", $i
}
print NL
}' /path/to/your/file
substr
Sposób:
$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file
(tcdyl wymyślił lepszą metodę w swojej odpowiedzi )
Myślę, że sed
rozwiązanie wynika naturalnie z pozostałych, ale zaczyna robić się absurdalnie długie
awk
to twój najlepszy zakład. awk
drukuje pola według numeru, więc ...
awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file
Aby usunąć kolumnę, nie drukuj jej:
awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file
Aby zmienić kolejność:
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file
Przekieruj do pliku wyjściowego.
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file
awk
może również sformatować dane wyjściowe.
Biorąc pod uwagę plik rozdzielany spacjami w następującym formacie:
1 2 3 4 5
Możesz usunąć pole 2 za pomocą awk tak:
awk '{ sub($2,""); print}' file
który zwraca
1 3 4 5
W razie potrzeby zastąp kolumnę 2 kolumną n.
Aby powielić kolumnę 2,
awk '{ col = $2 " " $2; $2 = col; print }' file
który zwraca
1 2 2 3 4 5
Aby przełączyć kolumny 2 i 3,
awk '{temp = $2; $2 = $3; $3 = temp; print}'
który zwraca
1 3 2 4 5
awk jest ogólnie bardzo dobry w radzeniu sobie z koncepcją pól . Jeśli masz do czynienia z plikiem CSV, a nie plikiem rozdzielanym spacjami, możesz po prostu użyć
awk -F,
aby zdefiniować pole jako przecinek zamiast spacji (która jest domyślna). Istnieje wiele dobrych zasobów awk online, z których jeden wymieniłem jako źródło poniżej.
Źródło dla # 3
awk
, ale wydaje się, że generuje separację spacji, nawet jeśli separator pól jest ,
(separator pól kontroluje tylko sposób, w jaki obsługuje dane wejściowe)
Będzie to działać w przypadku usuwania
awk '{$2="";$0=$0;$1=$1}1'
Wkład
a b c d
Wydajność
a c d