Czy można zastąpić wartość komórki w pliku csv za pomocą grep,sed lub innego

0

Pytanie

Napisałem następujące polecenie

#!/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

Przykład wprowadzania pliku.csv

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

Kojący $newvalue=3 ,$rownum=4 i col=1, wtedy powyższy kod zastąpi:

Wymagana Wydajność

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

Tak więc, jeśli wiem wiersza i kolumny, czy można wymienić określoną wartość za pomocą grep, sed?

Edit1: w Polu 3 zawsze będzie mieć unikatową wartość dla poszczególnych wierszy. ( w przypadku, jeśli ta informacja i tak pomoże)

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

Najlepsza odpowiedź

1

Zakładając, że twój plik CSV jest tak samo proste, jak i to, co pokazujesz (bez przecinków w polach w cudzysłowie), i na newvalue nie zawiera znaków, które sed interpretował by w szczególny sposób (np. znaki ampersand, ukośniki lub odwrotne ukośniki), następujący powinno działać tylko z sed (testowane z GNU sed):

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

DEMONSTRACJA:

$ 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

Wyjaśnienia: $rownum jest używany jako adres (tutaj numer wiersza), w którym należy zastosować następujące polecenie. s jest poleceniem wymiany sed. [^,]* jest wyrażeniem regularnym do wyszukiwania i zamiany: maksymalnie długi ciąg, który nie zawiera przecinka. $newvalue jest lub wierszem. $col jest to wydarzenie, które trzeba wymienić.

Jeśli newvalue może zawierać znaki ampersand, ukośniki lub odwrotne ukośniki, które musimy najpierw oczyścić:

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

DEMONSTRACJA:

$ 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

To naprawdę działa. Kilka wskazówek: nie wiedziałem do tej odpowiedzi " [ ^ ,]*", ale jeśli sed może zastąpić pewną komórkę, to dlaczego mamy to [ ^ ,]* . Próbowałem sed -Ei "$rownum s/$newvalue/$col" file.csv i to spowodowało błąd, ale chciałbym dowiedzieć się o tym więcej. Również byłby przydatny dowolny zasób do czytania.
Helium

Musimy ` [ ^ ,]*`, bo to jest to, co określa, co to jest komórka. sed-to nie CSV-procesor to procesor tekstu w dowolnym formacie. W ten sposób, on nie wie, co to jest to, co nazywasz komórką. Musimy o tym opowiedzieć. Zespół wymiany sed (sszczegółowo wyjaśnione w instrukcji sed, które można łatwo znaleźć (jeśli jesteś pod GNU/Linux lub macOS, spróbuj man sed lub, co jeszcze lepsze, info sed). Zespół wymiany, którą próbowałem, syntaktycznie jest niepoprawna, dlatego wystąpił błąd.
Renaud Pacalet

Tak, teraz to ma więcej sensu, jeśli tak powiedzieć.
Helium
1

Z sedjak co:

#!/bin/bash

newvalue=3
rownum=4
col=1

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

Wynik file.csv

Header,1
Field1,Field2,Field3
1,ABC,4567
3,XYZ,7890
  • ${rownum} jest zgodny z numerem wiersza.
  • (([^,]+,){n}) odpowiada n-krotnym powtarzaniu grupy znaków bez przecinkiem, po którym następuje przecinek. Więc to musi być podciąg przed docelowym (podlegającym wymianie) kolumną przez przypisanie nDla col - 1.
2021-11-24 07:21:19

mimo, że to naprawdę działa, czy nie jest to trochę bardziej skomplikowany sposób działania, w porównaniu z odpowiedzią Renault. Na przykład, dlaczego musimy porównać powtarzanie n razy, jeśli możemy zamiast tego bezpośrednio go zastąpić? Tym nie mniej przydatne
Helium
0

Spróbujmy zrealizować polecenie sed

Rozważmy przykład pliku CSV z następującą zawartością:

$ cat file

Solaris,25,11
Ubuntu,31,2
Fedora,21,3
LinuxMint,45,4
RedHat,12,5
  1. Aby usunąć 1-e pola lub kolumny :
$ sed 's/[^,]*,//' file

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

To wyrażenie regularne wyszukuje ciąg znaków bez przecinka([^,]*) i usuwa je, co prowadzi do usunięcia 1-go pola.

  1. Aby wydrukować tylko ostatniego pola LUB usunąć wszystkie pola, z wyjątkiem ostatniego pola:
$ sed 's/.*,//' file

11
2
3
4
5

To wyrażenie regularne usuwa wszystko, aż do ostatniej przecinkami (.*,), co powoduje usunięcie wszystkich pól z wyjątkiem ostatniego pola.

  1. Do druku tylko 1-go pola:
$ sed 's/,.*//' file

Solaris
Ubuntu
Fedora
LinuxMint
RedHat

To wyrażenie regularne(,.*) usuwa znaki od 1 przecinku do końca, w wyniku czego zostaną usunięte wszystkie pola, z wyjątkiem ostatniego pola.

  1. Aby usunąć 2-e pole:
$ sed 's/,[^,]*,/,/' file

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

Wyrażenie regularne (,[^,]*,) wyszukuje się przecinkiem i sekwencji znaków, po których następuje przecinek, co prowadzi do wystąpienia z 2-m kolumną, i zastępuje ten szablon jest odpowiedni tylko przecinkiem, ostatecznie kończy się usunięciem 2-giej kolumny.

Uwaga: Usuwanie pól w środku staje się bardziej skomplikowana w sed, tak jak każde pole musi być powiązana dosłownie.

  1. Aby wydrukować tylko 2-go pola:
$ sed 's/[^,]*,\([^,]*\).*/\1/' file

25
31
21
45
12

Wyrażenie regularne jest zgodny z pierwszego pola, drugiego pola i innym, jednak grupuje tylko 2-e pole. Cały wiersz jest teraz zastąpiony 2-m polem(\1), więc pojawia się tylko 2-e pole.

  1. Należy używać tylko wiersze, w których ostatnia kolumna jest cyfra:
$ sed -n '/.*,[0-9]$/p' file

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

Wyrażenie regularne (,[0-9]$) sprawdza jednej cyfry w ostatnim polu, i zespół p wyświetla wiersz spełniający ten warunek.

  1. Aby ponumerować wszystkie wiersze w pliku:
$ 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

To imitacja polecenia cat-n. awk sprawia, że jest łatwo, za pomocą specjalnej zmiennej NR. Zespół " = " sed określa numer wiersza każdego wiersza, po którym następuje sama linia. Dane wyjściowe sed przekazywane są przekazywane w potoku w inne polecenie sed do połączenia się co 2 wiersze.

  1. Wymień ostatnie pole na 99, jeśli 1 - e pole - "Ubuntu".:
$ sed 's/\(Ubuntu\)\(,.*,\).*/\1\299/' file

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

To wyrażenie regularne odpowiada "Ubuntu" i do końca, za wyjątkiem ostatniej kolumny, a także grupuje każdy z nich. W części przeznaczonej do wymiany są zastępowane 1-i 2 grupy wraz z nowym numerem 99.

  1. Usuń 2-e pole, jeśli 1-e pole "RedHat":
$ sed 's/\(RedHat,\)[^,]*\(.*\)/\1\2/' file

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

1. pole "RedHat", 2-e pole, a pozostałe pola są pogrupowane i wymiana odbywa się tylko 1 i ostatniej grupy , co prowadzi do usunięcia 2-go pola.

  1. Aby wstawić nową kolumnę w końcu(ostatnia kolumna) :
$ sed 's/.*/&,A/' file

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

Wyrażenie regularne (.*) odpowiada całej linii i zastępuje ją najniższej ciągiem znaków (&) i nowym polem.

  1. Aby wstawić nową kolumnę na początek(1 - sza kolumna):
$ sed 's/.*/A,&/' file

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

Podobnie jak w poprzednim przykładzie, tylko za co zbiega się wierszem należy nową kolumnę

Mam nadzieję, że to pomoże. Daj mi znać, jeśli potrzebujesz użyć Awk lub jakąkolwiek inną drużynę. Dziękuję

2021-11-24 07:36:29

dzięki za szczegółowe wyjaśnienie, ale niestety to nie rozwiązuje problemu.
Helium

W innych językach

Ta strona jest w innych językach

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