Es posible reemplazar el valor de una celda en un archivo csv con grep,sed o ambos

0

Pregunta

He escrito el siguiente comando

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

Ejemplo de Entrada de archivo.csv

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

Assuiming $newvalue=3 ,$rownum=4 y col=1, entonces el código de arriba va a reemplazar:

Se Requiere De Salida

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

Por lo que si sé la fila y la columna, es posible que sustituya a dicho valor el uso de grep, sed?

Edit1: Campo3 siempre tendrá un valor único para sus respectivas filas. ( en caso de que la información de ayuda de todos modos)

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

Mejor respuesta

1

Suponiendo que el archivo CSV es tan simple como lo que se muestra (no comas en los citados campos), y su newvalue no contiene caracteres que sed iba a interpretar de una manera especial (por ejemplo, el signo, guiones o barras diagonales inversas), el siguiente debe trabajar solo con sed (probado con 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

Explicaciones: $rownum se utiliza como dirección (aquí el número de línea) donde aplicar el siguiente comando. s es la sed comando sustituir. [^,]* es la expresión regular para buscar y reemplazar: la larga cadena que no contiene una coma. $newvalue es la cadena de reemplazo. $col es la ocurrencia de sustituir.

Si newvalue puede contener signos, guiones o barras diagonales inversas debemos desinfectar primero:

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

Esto hace el trabajo. Sólo un par de punteros aunque: yo no era consciente antes de esta respuesta de ` [^,]*` pero si la sed es capaz de reemplazar a una celda específica, entonces ¿por qué estamos incluyendo [^,]* . Me hizo intentar sed -Ei "$rownum s/$newvalue/$col" file.csv y se tiró un error, pero le gustaría saber más acerca de esto. Cualquier recurso a leer a podría ser útil también.
Helium

Necesitamos ` [ ^ ,]*, porque es lo que define lo que es un celular es. la sed no es un CSV procesador, es una en cualquier procesador de texto. Así que no tiene conocimiento de lo que usted llame a un celular es. Debemos contarla. La sed comando sustituir (s) se explica en profundidad los detalles de la sed manual que encontrarás fácilmente (si es menor de GNU/Linux o macOS intentar man sed o, mejor aún, info sed). El comando sustituir intentado es sintácticamente incorrecta, por lo tanto el error.
Renaud Pacalet

Sí, eso tiene más sentido ahora, cuando se pone así.
Helium
1

Con sed, cómo acerca de:

#!/bin/bash

newvalue=3
rownum=4
col=1

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

Resultado de file.csv

Header,1
Field1,Field2,Field3
1,ABC,4567
3,XYZ,7890
  • ${rownum} coincide con el número de línea.
  • (([^,]+,){n}) coincide con el n de tiempo de la repetición de un grupo de no coma caracteres seguidos por una coma. Entonces debe ser la subcadena antes de la meta (para ser sustituido) de la columna mediante la asignación de na col - 1.
2021-11-24 07:21:19

aunque esto funciona, no es este un poco más complicada manera de hacer las cosas en comparación a cómo Renauld la respuesta. Como, ¿por qué necesitamos para que coincida con el n de tiempo de repetición si por el contrario, podemos reemplazar directamente de él? Útil, sin embargo
Helium
0

Vamos a Tratar de Implementar el comando sed

Vamos a considerar un ejemplo de archivo CSV con el siguiente contenido:

$ cat file

Solaris,25,11
Ubuntu,31,2
Fedora,21,3
LinuxMint,45,4
RedHat,12,5
  1. Para quitar el 1er campo o columna :
$ sed 's/[^,]*,//' file

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

Esta expresión regular busca una secuencia de no coma([^,]*) caracteres y elimina lo que resulta en el 1er campo de llegar eliminado.

  1. Para imprimir sólo el último campo, O eliminar todos los campos, excepto el último campo:
$ sed 's/.*,//' file

11
2
3
4
5

Esta expresión quita todo, hasta la última coma(.*,) lo que resulta en la eliminación de todos los campos, excepto el último campo.

  1. Para imprimir sólo el 1 campo:
$ sed 's/,.*//' file

Solaris
Ubuntu
Fedora
LinuxMint
RedHat

Esta expresión(,.*) elimina los caracteres a partir del 1 de coma hasta el final, resultando en la eliminación de todos los campos, excepto el último campo.

  1. Para eliminar el 2º campo:
$ sed 's/,[^,]*,/,/' file

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

El regex (,[^,]*,) busca una coma y la secuencia de caracteres seguidos por una coma que se traduce en la adecuación de la 2ª columna, y sustituye a este patrón coincide con sólo una coma, en última instancia, termina en la eliminación de la 2ª columna.

Nota: Para borrar los campos en el medio se vuelve más más difícil en el sed, ya que cada campo tiene que ser emparejado literalmente.

  1. Para imprimir sólo el 2º campo:
$ sed 's/[^,]*,\([^,]*\).*/\1/' file

25
31
21
45
12

La expresión regular coincide con el primer campo, el segundo campo y el resto, sin embargo los grupos de la 2ª campo solo. Toda la línea es ahora reemplazado con el 2º campo(\1), por lo que sólo el 2º campo se muestra.

  1. Imprimir sólo las líneas en la que la última columna es un número de un solo dígito:
$ sed -n '/.*,[0-9]$/p' file

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

El regex (,[0-9]$) controles de un solo dígito en el último campo y el comando p imprime la línea que coincida con esta condición.

  1. El número de todas las líneas en el archivo:
$ 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

Esta es la simulación de cat-n de comandos. awk hace fácilmente mediante la variable especial NR. El '=' comando de la sed que da el número de línea de cada línea seguida por la misma línea. La sed de salida se transfiere a otro comando sed para unir cada 2 líneas.

  1. Sustituir el último campo por 99 si el 1 es el de "Ubuntu':
$ sed 's/\(Ubuntu\)\(,.*,\).*/\1\299/' file

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

Esta expresión coincide con 'Ubuntu' y hasta el final, excepto en la última columna y los grupos de cada uno de ellos. En la parte de reemplazo, el 1 y el grupo 2 junto con el nuevo número de 99 es sustituido.

  1. Borrar el 2º campo si el 1 es el de "RedHat':
$ sed 's/\(RedHat,\)[^,]*\(.*\)/\1\2/' file

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

El 1er campo "RedHat", el 2º campo y el resto de los campos se agrupan, y el reemplazo se realiza con sólo 1 y el último grupo , resuting en conseguir el 2º campo eliminado.

  1. Para insertar una nueva columna al final(última columna) :
$ sed 's/.*/&,A/' file

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

El regex (.*) coincide con la línea completa y reemplazarla con la propia línea (&) y el nuevo campo.

  1. Para insertar una nueva columna en el principio(1ª columna):
$ sed 's/.*/A,&/' file

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

Mismo como último ejemplo, solo la línea coincidente es seguido por la nueva columna

Espero que esto ayude. Déjeme saber si usted necesita usar Awk o cualquier otro comando. Gracias

2021-11-24 07:36:29

gracias por la detallada explicación, pero lamentablemente esto no soluciona el problema a la mano.
Helium

En otros idiomas

Esta página está en otros idiomas

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