Bir csv dosyasındaki bir hücrenin değerini grep,sed veya her ikisini kullanarak değiştirmek mümkün mü

0

Soru

Aşağıdaki komutu yazdım

#!/bin/bash
awk -v value=$newvalue -v row=$rownum -v col=1 'BEGIN{FS=OFS=","} NR==row {$col=value}1' "${file}".csv >> temp.csv && mv temp.csv "${file}".csv

Örnek dosya girişi.csv dosyası

Header,1
Field1,Field2,Field3
1,ABC,4567
2,XYZ,7890

Tahlil $newvalue=3 ,$rownum= 4 ve col=1, daha sonra yukarıdaki kod yerini alacak:

Gerekli Çıktı

Header,1
Field1,Field2,Field3
1,ABC,4567
3,XYZ,7890

Yani satırı ve sütunu biliyorsam, söz konusu değeri grep, sed kullanarak değiştirmek mümkün mü?

Edit1: Field3 her zaman kendi satırları için benzersiz bir değere sahip olacaktır. ( bu bilginin yine de yardımcı olması durumunda)

bash csv git-bash linux
2021-11-24 06:52:47
3

En iyi cevabı

1

CSV dosyanızın gösterdiğiniz kadar basit olduğunu varsayarsak (alıntılanan alanlarda virgül yok) ve sizin newvalue sed'in özel bir şekilde yorumlayacağı karakterler içermez (örn. ampersanlar, eğik çizgiler veya ters eğik çizgiler), aşağıdakiler yalnızca sed ile çalışmalıdır (GNU sed ile test edilmiştir):

sed -Ei "$rownum s/[^,]*/$newvalue/$col" file.csv

Gösteri:

$ cat file.csv
Header,1
Field1,Field2,Field3
1,ABC,4567
3,XYZ,7890
$ rownum=3
$ col=2
$ newvalue="NEW"
$ sed -Ei "$rownum s/[^,]*/$newvalue/$col" file.csv
$ cat file.csv
Header,1
Field1,Field2,Field3
1,NEW,4567
3,XYZ,7890

Açıklamalar: $rownum aşağıdaki komutun uygulanacağı adres (burada satır numarası) olarak kullanılır. s sed değiştirme komutudur. [^,]* aranacak ve değiştirilecek normal ifadedir: virgül içermeyen mümkün olan en uzun dize. $newvalue yedek dizedir. $col değiştirilmesi gereken olaydır.

Eğer newvalue önce sterilize etmemiz gereken ampersanlar, eğik çizgiler veya ters eğik çizgiler içerebilir:

sanitizednewvalue=$(sed -E 's/([/\&])/\\\1/g' <<< "$newvalue")
sed -Ei "$rownum s/[^,]*/$sanitizednewvalue/$col" file.csv

Gösteri:

$ newvalue='NEW&\/&NEW'
$ sanitizednewvalue=$(sed -E 's/([/\&])/\\\1/g' <<< "$newvalue")
$ echo "$sanitizednewvalue"
NEW\&\\\/\&NEW
$ sed -Ei "$rownum s/[^,]*/$sanitizednewvalue/$col" file.csv
$ cat file.csv
Header,1
Field1,Field2,Field3
1,NEW&\/&NEW,4567
3,XYZ,7890
2021-11-24 11:13:43

Bu işe yarar. Sadece birkaç işaretçi olsa da: ` [ ^ ,] * ' nin bu cevabından önce farkında değildim, ancak sed belirli bir hücrenin yerini alabiliyorsa, neden [ ^ ,] * dahil ediyoruz . Denedim sed -Ei "$rownum s/$newvalue/$col" file.csv ve bir hata attı ama bu konuda daha fazla bilgi edinmek istiyorum. Okumak için herhangi bir kaynak da yararlı olacaktır.
Helium

Biz gerek ` [^,]*` çünkü ne tanımlar ne bir cep var. sed bir CSV işlemcisi değil, herhangi bir metin işlemcisidir. Yani hücre dediğinizin ne olduğu hakkında hiçbir bilgisi yok. Bunu söylemek zorundayız. Sed değiştirme komutu (s) kolayca bulabileceğiniz sed kılavuzunda ayrıntılı olarak açıklanmıştır (GNU/Linux veya macOS altındaysanız deneyin man sed veya daha iyisi , info sed). Denediğiniz yedek komut sözdizimsel olarak yanlıştır, bu nedenle hatadır.
Renaud Pacalet

Evet, şimdi böyle ifade etmek daha mantıklı.
Helium
1

İle sed ne dersin :

#!/bin/bash

newvalue=3
rownum=4
col=1

sed -i -E "${rownum} s/(([^,]+,){$((col-1))})[^,]+/\\1${newvalue}/" file.csv

Sonucu file.csv

Header,1
Field1,Field2,Field3
1,ABC,4567
3,XYZ,7890
  • ${rownum} satır numarasıyla eşleşiyor.
  • (([^,]+,){n}) grubun n-zaman tekrarı ile eşleşir virgülsüz karakterler ve ardından virgül. O zaman alt dize olmalı hedeften önce (değiştirilecek) sütunu atayarak n -e doğru col - 1.
2021-11-24 07:21:19

bu işe yarasa da, Renauld'un cevabına kıyasla işleri yapmanın biraz daha karmaşık bir yolu değil mi? Örneğin, bunun yerine doğrudan değiştirebilirsek, neden n-zaman tekrarını eşleştirmemiz gerekiyor? Yine de yararlı
Helium
0

Sed komutunu Uygulamaya çalışalım

Aşağıdaki içeriğe sahip örnek bir CSV dosyası düşünelim:

$ cat file

Solaris,25,11
Ubuntu,31,2
Fedora,21,3
LinuxMint,45,4
RedHat,12,5
  1. 1. Alanı veya sütunu kaldırmak için :
$ sed 's/[^,]*,//' file

25,11
31,2
21,3
45,4
12,5

Bu normal ifade, virgülsüz ([^,]*) karakter dizisini arar ve bunları siler, bu da 1.alanın kaldırılmasına neden olur.

  1. Yalnızca son alanı yazdırmak VEYA son alan dışındaki tüm alanları kaldırmak için:
$ sed 's/.*,//' file

11
2
3
4
5

Bu regex son virgüle kadar her şeyi kaldırır(.* ,) son alan dışındaki tüm alanların silinmesine neden olur.

  1. Yalnızca 1. alanı yazdırmak için:
$ sed 's/,.*//' file

Solaris
Ubuntu
Fedora
LinuxMint
RedHat

Bu regex(,.* ) 1. virgülden başlayarak sonuna kadar olan karakterleri kaldırır ve son alan dışındaki tüm alanların silinmesine neden olur.

  1. 2. Alanı silmek için:
$ sed 's/,[^,]*,/,/' file

Solaris,11
Ubuntu,2
Fedora,3
LinuxMint,4
RedHat,5

Regex (, [ ^ ,]*,) bir virgül ve ardından 2.sütunun eşleşmesiyle sonuçlanan bir karakter dizisi arar ve eşleşen bu kalıbı yalnızca virgülle değiştirir ve sonuçta 2. sütunun silinmesiyle sonuçlanır.

Not: Ortadaki alanları silmek, her alanın tam anlamıyla eşleştirilmesi gerektiğinden sed'de daha da zorlaşır.

  1. Yalnızca 2. alanı yazdırmak için:
$ sed 's/[^,]*,\([^,]*\).*/\1/' file

25
31
21
45
12

Regex ilk alanla, ikinci alanla ve geri kalanıyla eşleşir, ancak 2.alanı tek başına gruplandırır. Tüm satır şimdi 2.alanla(\1) değiştirilir, bu nedenle yalnızca 2. alan görüntülenir.

  1. Yalnızca son sütunun tek basamaklı bir sayı olduğu satırları yazdırın:
$ sed -n '/.*,[0-9]$/p' file

Ubuntu,31,2
Fedora,21,3
LinuxMint,45,4
RedHat,12,5

Regex (, [0-9]$) son alanda tek bir basamak olup olmadığını denetler ve p komutu bu koşulla eşleşen satırı yazdırır.

  1. Dosyadaki tüm satırları numaralandırmak için:
$ sed = file | sed 'N;s/\n/ /'

1 Solaris,25,11
2 Ubuntu,31,2
3 Fedora,21,3
4 LinuxMint,45,4
5 RedHat,12,5

Bu cat-n komutunun simülasyonu. awk bunu özel değişken nr'yi kullanarak kolayca yapar. Sed'in ' = ' komutu, her satırın satır numarasını ve ardından satırın kendisini verir. Sed çıktısı, her 2 satırı birleştirmek için başka bir sed komutuna bağlanır.

  1. 1. alan 'Ubuntu'ise son alanı 99 ile değiştirin:
$ sed 's/\(Ubuntu\)\(,.*,\).*/\1\299/' file

Solaris,25,11
Ubuntu,31,99
Fedora,21,3
LinuxMint,45,4
RedHat,12,5

Bu regex 'Ubuntu' ile eşleşir ve son sütun hariç sonuna kadar ve her birini de gruplar. Yedek parçada, yeni 99 numara ile birlikte 1. ve 2. grup ikame edilir.

  1. 1. alan 'RedHat'ise 2. alanı silin:
$ sed 's/\(RedHat,\)[^,]*\(.*\)/\1\2/' file

Solaris,25,11
Ubuntu,31,2
Fedora,21,3
LinuxMint,45,4
RedHat,,5

1. alan 'RedHat', 2. alan ve kalan alanlar gruplandırılır ve değiştirme yalnızca 1. ve son grupla yapılır ve 2. alanın silinmesine neden olur.

  1. Sonuna yeni bir sütun eklemek için (son sütun) :
$ sed 's/.*/&,A/' file

Solaris,25,11,A
Ubuntu,31,2,A
Fedora,21,3,A
LinuxMint,45,4,A
RedHat,12,5,A

Regex (.* ) tüm satırı eşleştirir ve satırın kendisi (&) ve yeni alanla değiştirir.

  1. Başa yeni bir sütun eklemek için (1. sütun):
$ sed 's/.*/A,&/' file

A,Solaris,25,11
A,Ubuntu,31,2
A,Fedora,21,3
A,LinuxMint,45,4
A,RedHat,12,5

Son örnekle aynı şekilde, yalnızca eşleşen satırı yeni sütun izler

Bu yardımcı olacağını umuyoruz. Awk veya başka bir komut kullanmanız gerekirse bana bildirin. Teşekkür ederim

2021-11-24 07:36:29

ayrıntılı açıklama için teşekkürler ama ne yazık ki eldeki sorunu çözmüyor.
Helium

Diğer dillerde

Bu sayfa diğer dillerde

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................