Este posibil de a înlocui valoarea unei celule într-un fișier csv folosind grep,sed sau ambele

0

Problema

Am scris următoarea comandă

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

Eșantionul de Intrare de fișier.csv

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

Assuiming $newvalue=3 ,$rownum=4 și col=1, atunci codul de mai sus va înlocui:

Ieșire Necesare

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

Deci, dacă eu știu de rând și de coloană, este posibil să se înlocuiască spus valoarea folosind grep, sed?

Edit1: Field3 va avea întotdeauna o valoare unică pentru propriile rânduri. ( în cazul în care informații ajută oricum)

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

Cel mai bun răspuns

1

Presupunând că fișierul CSV este la fel de simplu ca ceea ce se arată (fără virgule în citat domenii), și newvalue nu conține caractere care sed ar interpreta într-un mod special (de exemplu, ampersand, slash-uri sau backslash-uri), următoarele ar trebui să funcționeze cu doar sed (testat cu GNU sed):

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

Demo:

$ 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

Explicații: $rownum este folosit ca adresă (aici numărul liniei) în cazul în care să se aplice următoarea comandă. s este sed substitut de comandă. [^,]* este o expresie regulată pentru a căuta și înlocui: cel mai lung șir care nu conține o virgulă. $newvalue este șirul de înlocuire. $col este apariția pentru a înlocui.

Dacă newvalue poate conține ampersand, slash-uri sau backslash-uri trebuie să-l steriliza în primul rând:

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

Demo:

$ 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

Acest lucru nu funcționează. Doar câteva indicii, deși: nu am fost conștient înainte de acest răspuns al ` [^,]*` dar dacă sed este capabil să înlocuiască pentru o anumită celulă, atunci de ce ne-inclusiv [^,]* . Am încercat sed -Ei "$rownum s/$newvalue/$col" file.csv și ea a aruncat o eroare, dar Ar dori să știu mai multe despre asta. Orice resursă pentru a citi printr-ar fi de ajutor.
Helium

Avem nevoie de [^,]*` pentru că aceasta este ceea ce definește ceea ce o celulă este. sed nu este un CSV procesor, este o orice procesor de text. Așa că nu are cunoștință de ceea ce numim o celulă este. Trebuie să-l spun. Sed substitut de comandă (s) este explicat în detalii profunde în sed manual care te va găsi cu ușurință (dacă sunt sub GNU/Linux sau macOS încercați man sed sau, chiar mai bine, info sed). Substitut de comandă ai încercat punct de vedere sintactic este incorectă, astfel de eroare.
Renaud Pacalet

Da, asta face mai mult sens acum, atunci când pune așa.
Helium
1

Cu sedcum despre:

#!/bin/bash

newvalue=3
rownum=4
col=1

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

Rezultatul file.csv

Header,1
Field1,Field2,Field3
1,ABC,4567
3,XYZ,7890
  • ${rownum} se potrivește cu numărul de linie.
  • (([^,]+,){n}) meciurile n-timp de repetiție a grupului de non-virgulă caractere urmat de o virgulă. Atunci ar trebui să fie subșirul înainte de țintă (să fie înlocuit) coloana prin atribuirea npentru a col - 1.
2021-11-24 07:21:19

chiar dacă acest lucru nu funcționează, nu este acest lucru un pic mai complicat mod de a face lucrurile față de cum Renauld e răspunsul. Ca de ce avem nevoie pentru a se potrivi n-timp de repetiție dacă putem în schimb direct înlocui? Cu toate acestea, utile
Helium
0

Să Încercăm să pună în Aplicare sed comanda

Să considerăm un exemplu de fișier CSV cu următorul conținut:

$ cat file

Solaris,25,11
Ubuntu,31,2
Fedora,21,3
LinuxMint,45,4
RedHat,12,5
  1. Pentru a elimina 1 câmp sau coloană :
$ sed 's/[^,]*,//' file

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

Această expresie regulată căutări pentru o secvență de non-virgulă([^,]*) caractere și le șterge ceea ce duce la 1 domeniul obtinerea eliminat.

  1. Pentru a imprima numai ultimul câmp, SAU de a elimina toate câmpurile, cu excepția ultimului domeniu:
$ sed 's/.*,//' file

11
2
3
4
5

Acest regex elimină totul până la ultima virgulă(.*,) ceea ce duce la ștergerea toate domeniile, cu excepția ultimul câmp.

  1. Pentru a imprima doar 1 domeniu:
$ sed 's/,.*//' file

Solaris
Ubuntu
Fedora
LinuxMint
RedHat

Acest regex(,.*) elimină caractere începând de la 1 virgulă până la sfârșitul rezultă în ștergerea toate domeniile, cu excepția ultimului câmp.

  1. Pentru a șterge a 2-a domeniul:
$ sed 's/,[^,]*,/,/' file

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

Regex (,[^,]*,) căutări pentru o virgulă și secvență de caractere urmat de o virgulă, care rezultă în potrivire a 2-a coloana, și înlocuiește acest model asortat cu doar o virgulă, în cele din urmă se încheie în ștergerea 2-a coloană.

Notă: Pentru a șterge câmpurile din mijloc devine mai dure în sed deoarece fiecare câmp trebuie să fie adaptate la propriu.

  1. Pentru a imprima numai a 2-a domeniul:
$ sed 's/[^,]*,\([^,]*\).*/\1/' file

25
31
21
45
12

Regex meciuri primul câmp, câmpul al doilea, iar restul, cu toate acestea, grupuri de 2 domeniul singur. Întreaga linie este acum înlocuit cu al 2-lea domeniu(\1), prin urmare, doar al 2-lea câmp se afișează.

  1. Imprimați numai linii în care ultima coloană este un singur număr în cifre:
$ sed -n '/.*,[0-9]$/p' file

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

Regex (,[0-9]$) verifică o singură cifră în ultimul câmp și p comanda printuri de linie care se potrivește cu această condiție.

  1. Să număr toate liniile din fișier:
$ 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

Aceasta este o simulare de pisica-n comandă. awk face cu ușurință folosind variabilă special NR. A '=' comanda sed dă numărul de linie din fiecare linie, urmată de linia în sine. Sed ieșire este adus la un alt sed comandă să se alăture fiecare 2 linii.

  1. Înlocuiți ultimul câmp de 99 dacă 1 domeniu este 'Ubuntu':
$ sed 's/\(Ubuntu\)\(,.*,\).*/\1\299/' file

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

Acest regex meciuri 'Ubuntu' și până la sfârșit, cu excepția ultima coloană și grupuri de fiecare dintre ei. În piesa de schimb, 1 și 2 grup împreună cu noul număr de 99 este înlocuit.

  1. Ștergeți al 2-lea câmp dacă 1 domeniu este 'RedHat':
$ sed 's/\(RedHat,\)[^,]*\(.*\)/\1\2/' file

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

1 domeniul de RedHat', al 2-lea domeniu și câmpurile rămase sunt grupate, iar înlocuirea se face cu doar 1 iar ultimul grup , resuting în obținerea a 2-a domeniul șterse.

  1. Pentru a insera o nouă coloană la final(ultima coloană) :
$ 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 (.*) meciuri întreaga linie și înlocuirea acesteia cu linia în sine (&) și noul domeniu.

  1. Pentru a insera o nouă coloană la început(1 coloana):
$ sed 's/.*/A,&/' file

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

La fel ca ultima exemplu, doar linie de potrivire este urmată de o nouă coloană

Sper acest lucru vă va ajuta. Lasă-mă să știu dacă aveți nevoie pentru a utiliza Awk sau orice alta comanda. Multumesc

2021-11-24 07:36:29

mulțumesc pentru explicație detaliată, dar, din păcate, nu rezolva problema de la mână.
Helium

În alte limbi

Această pagină este în alte limbi

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