Bir CSV dosyasını sed veya awk ile nasıl değiştirebilirim?


23

Aşağıdakileri sedveya kullanarak bir CSV dosyasına nasıl yapabilirim awk?

  • Bir sütunu sil
  • Bir sütunu çoğalt
  • Bir sütunu taşı

200'den fazla satırdan oluşan büyük bir masam var ve o kadar aşina değilim sed.


1
Çapraz Ask Ubuntu yayınlanan
enzotib

@enzotib linkini gönderebilir misiniz?
n0pe

@MaxMackie askubuntu.com/questions/88142/… . Bu saatte orada bir mod alamıyorum, bu yüzden istekli olup olmadıklarını göç etmelerini istemekle işaretledim; Zaten kabul edilmiş bir cevabı var bu yüzden yapacaklarından emin değilim
Michael Mrozek

@MichaelMrozek, hmmm bu durumlarda genellikle ne olur? Sadece kopyaları saklıyor muyuz?
n0pe

1
Yalnızca temel araçları olan bir sistemde çalıştırmanız gerekmiyorsa, bkz. Csv dosyalarını işlemek için sağlam bir komut satırı aracı var mı?
Gilles 'SO- kötülük'

Yanıtlar:


7

Alanların nasıl kesileceği ve yeniden düzenleneceği (diğer cevaplarda ele alınan) bir yana, ilginç CSV alanları da var.

Verileriniz bu "ilginç" kategoriye girerse, bir miktar ön ve son filtreleme bununla ilgilenebilir. Aşağıda gösterilen filtreler karakterler gerektiren \x01, \x02, \x03, \x04verilerinizin herhangi bir yerinde görünmemesine.

İşte basit bir awkçöplük etrafına sarılı filtreler .

Not: Beşinci alanın geçersiz / eksik "alıntı alanı" düzeni var, ancak satırın sonunda (CSV ayrıştırıcısına bağlı olarak) iyi huylu. Ancak, elbette, şu anki satır sonundan uzağa kaydırılırsa , sorunlu, beklenmeyen sonuçlara yol açacaktır .

Güncelleştirme; user121196 , virgül sondaki bir alıntıdan önce geldiğinde bir hata olduğunu belirtti. İşte düzeltme.

Veri

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' 

Çıktı:

field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five

"15111 N. Hayden Rd., Ste 160,"
""

İşte yorumlar ile genişletilmiş ön filtre . Sonrası filtre sadece bir ters olan . , ,
\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
'

Bu filtreye dayanarak nth sütununu nasıl silersiniz?
user121196

@ user121196 - Açılış cümlesinde belirtildiği gibi, bu cevap CSV verilerini daha tutarlı kılmanın bir yolunu gösterir .. örn. Alıntı gömülü bir virgülün geçici olarak nötr bir simge ile değiştirilmesi ve daha sonra hareket / kesim / silme işleminden sonra tekrar virgül haline getirilmesi . Yine belirtildiği gibi, hareket / kes / sil adımı basit bir awk alan dökümü ile değiştirilir .
Peter.O

1
bu durumda başarısız olur: "15111 N. Hayden Rd., Ste 160,", ""
user121196

@ user121196: Bunu işaret ettiğiniz için teşekkür ederiz. Cevabı bir düzeltmeyle güncelledim.
Peter.O

15

Bu, CSV dosyanızın yalnızca sınırlayıcılar için virgül kullanıp kullanmadığına veya deliliğinizin olup olmamasına bağlıdır:

alan bir, "alan, iki", alan üç

Bu, basit bir CSV dosyası kullandığınızı varsayar:

Bir sütunu kaldırma

Tek bir sütundan kurtulmanın birçok yolu vardır; Sütun 2'yi örnek olarak kullandım. En kolay yol muhtemelen kullanmaktır cut, bu da bir sınırlayıcı -dve hangi alanları yazdırmak istediğinizi belirtmenizi sağlar -f; bu, virgüllere ve çıkış alanına 1 ve sonuna kadar alanları 3'e bölmesini söyler:

$ cut -d, -f1,3- /path/to/your/file

Gerçekten kullanmanız sedgerekirse, ilk n-1alanlarla, nth alanıyla ve kalanıyla eşleşen normal bir ifade yazabilir ve th çıktısını atlayabilirsiniz n(burada n2, yani ilk grup eşleşen 1zamandır \{1\}):

$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file

Bunu yapmanın çeşitli yolları var awk, hiçbiri özellikle şık değil. Bir fordöngü kullanabilirsiniz , ancak takip eden virgülle uğraşmak acı vericidir; böyle bir şey olacağını görmezden gelmek:

$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file

Alan 1'i çıkarmayı daha kolay buluyorum ve ardından substralan 2'den sonra her şeyi çıkarmak için kullanıyorum :

$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file

Bu olsa boyunca boyunca daha da sütunlar için can sıkıcı bir durum

Bir sütunu çoğaltma

Bu sedesasen öncekiyle aynı ifadedir, ancak aynı zamanda hedef sütunu yakalar ve yerine geçen grubu birkaç kez eklersiniz:

$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file

In awkiçin döngü yolu o (yine sondaki virgül görmezden) gibi bir şey olurdu:

$ awk -F, '{
for(i=1; i<=NF; i++) {
    if(i == 2) printf "%s,", $i;
    printf "%s,", $i
}
print NL
}' /path/to/your/file

substryol:

$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file

(tcdyl daha iyi yöntem ile geldi onun cevabını )

Bir sütunu taşıma

Bence sedçözüm diğerlerinden doğal olarak geliyor ama saçma sapmaya başladı


Bu dolu bir cevap! +1 :)
jaypal singh

Gülünç uzun mu? Pah !
Gilles 'SO- kötülük'

12

awkEn iyi bahis awkalanları sayıya göre yazdırır, bu yüzden ...

awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file

Bir sütunu kaldırmak için yazdırmayın:

 awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file

Siparişi değiştirmek için:

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file

Bir çıktı dosyasına tekrar yönlendirin.

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file

awk çıkışı da biçimlendirebilir.

Awk formatı çıkışı


CSV olduğundan, ayrıca ihtiyacınız olacak BEGIN { FS=","; OFS=","; }.

1
Ben bile FS = OFS = "," çalışacağını düşünüyorum.

5

Boşlukla ayrılmış bir dosyaya aşağıdaki biçimde verilir:

1 2 3 4 5

Alan 2'yi awk ile kaldırabilirsiniz:

awk '{ sub($2,""); print}' file

hangi döner

1  3 4 5

Uygunsa, sütun 2'yi sütun n ile değiştirin.

2. sütunu çoğaltmak için,

awk '{ col = $2 " " $2; $2 = col; print }' file

hangi döner

1 2 2 3 4 5

2. ve 3. sütunları değiştirmek için,

awk '{temp = $2; $2 = $3; $3 = temp; print}'

hangi döner

1 3 2 4 5

awk genellikle kavramı ile ilgili çok iyi olduğunu alanları . Bir CSV ile uğraşıyorsunuz ve boşlukla ayrılmış bir dosya değil, sadece

awk -F,

alanınızı boşluk yerine virgül olarak tanımlamak için (varsayılan ayardır). Çevrimiçi olarak bir tanesi aşağıda kaynak olarak listelediğim birkaç iyi awk kaynağı var.

# 3 kaynağı


Hakkında pek bir şey bilmiyorum awk, ancak alan ayırıcı olsa bile alandan ayrılmış çıktılar veriyor ,(alan ayırıcı sadece girdilerin nasıl işlendiğini kontrol ediyor)
Michael Mrozek

@MichaelMrozek: evet, çıkış alanı ayırıcısını kontrol eden OFS awk değişkenidir.
enzotib


0

Bu silmek için çalışacak

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

Giriş

a b c d

Çıktı

a c d
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.