Aşağıdakileri sed
veya 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
.
Aşağıdakileri sed
veya kullanarak bir CSV dosyasına nasıl yapabilirim awk
?
200'den fazla satırdan oluşan büyük bir masam var ve o kadar aşina değilim sed
.
Yanıtlar:
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
, \x04
verilerinizin 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, 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:
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ı -d
ve 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 sed
gerekirse, ilk n-1
alanlarla, n
th alanıyla ve kalanıyla eşleşen normal bir ifade yazabilir ve th çıktısını atlayabilirsiniz n
(burada n
2, yani ilk grup eşleşen 1
zamandı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 for
dö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 substr
alan 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
Bu sed
esasen ö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 awk
iç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
substr
yol:
$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file
(tcdyl daha iyi yöntem ile geldi onun cevabını )
Bence sed
çözüm diğerlerinden doğal olarak geliyor ama saçma sapmaya başladı
awk
En iyi bahis awk
alanları 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.
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ğı
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)
Bu silmek için çalışacak
awk '{$2="";$0=$0;$1=$1}1'
Giriş
a b c d
Çıktı
a c d