Можно ли заменить значение ячейки в csv-файле с помощью grep,sed или того и другого

0

Вопрос

Я написал следующую команду

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

Пример ввода файла.csv

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

Успокаивающее $newvalue=3 ,$rownum=4 и col=1, тогда приведенный выше код заменит:

Требуемая Производительность

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

Итак, если я знаю строку и столбец, можно ли заменить указанное значение с помощью grep, sed?

Edit1: Поле 3 всегда будет иметь уникальное значение для соответствующих строк. ( в случае, если эта информация все равно поможет)

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

Лучший ответ

1

Предполагая, что ваш CSV-файл так же прост, как и то, что вы показываете (без запятых в полях в кавычках), и ваш newvalue не содержит символов, которые sed интерпретировал бы особым образом (например, амперсанды, косые черты или обратные косые черты), следующее должно работать только с sed (проверено с GNU sed):

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

ДЕМОНСТРАЦИЯ:

$ 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

Объяснения: $rownum используется в качестве адреса (здесь номер строки), по которому следует применить следующую команду. s является командой замены sed. [^,]* является регулярным выражением для поиска и замены: максимально длинная строка, не содержащая запятой. $newvalue является заменяющей строкой. $col это событие, которое нужно заменить.

Если newvalue может содержать амперсанды, косые черты или обратные косые черты, которые мы должны сначала очистить:

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

ДЕМОНСТРАЦИЯ:

$ 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

Это действительно работает. Всего несколько указателей: я не знал до этого ответа " [ ^ ,]*", но если sed может заменить определенную ячейку, то почему мы включаем [ ^ ,]* . Я действительно пытался sed -Ei "$rownum s/$newvalue/$col" file.csv и это вызвало ошибку, но хотелось бы узнать об этом больше. Также был бы полезен любой ресурс для чтения.
Helium

Нам нужно ` [ ^ ,]*`, потому что это то, что определяет, что такое ячейка. sed-это не CSV-процессор, это текстовый процессор любого формата. Таким образом, он не знает, что такое то, что вы называете клеткой. Мы должны рассказать об этом. Команда замены sed (s) подробно объясняется в руководстве sed, которое вы легко найдете (если вы находитесь под GNU/Linux или macOS, попробуйте man sed или, что еще лучше, info sed). Команда замены, которую вы попробовали, синтаксически неверна, следовательно, произошла ошибка.
Renaud Pacalet

Да, теперь это имеет больше смысла, если так выразиться.
Helium
1

С sed, как насчет:

#!/bin/bash

newvalue=3
rownum=4
col=1

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

Результат file.csv

Header,1
Field1,Field2,Field3
1,ABC,4567
3,XYZ,7890
  • ${rownum} совпадает с номером строки.
  • (([^,]+,){n}) соответствует n-кратному повторению группы символов без запятой, за которыми следует запятая. Тогда это должна быть подстрока перед целевым (подлежащим замене) столбцом путем присвоения n Для col - 1.
2021-11-24 07:21:19

несмотря на то, что это действительно работает, не является ли это немного более сложным способом действий по сравнению с ответом Рено. Например, зачем нам нужно сопоставлять повторение n раз, если мы можем вместо этого напрямую заменить его? Тем не менее полезно
Helium
0

Давайте попробуем реализовать команду sed

Давайте рассмотрим пример CSV-файла со следующим содержимым:

$ cat file

Solaris,25,11
Ubuntu,31,2
Fedora,21,3
LinuxMint,45,4
RedHat,12,5
  1. Чтобы удалить 1-е поле или столбец :
$ sed 's/[^,]*,//' file

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

Это регулярное выражение ищет последовательность символов без запятой([^,]*) и удаляет их, что приводит к удалению 1-го поля.

  1. Для печати только последнего поля ИЛИ удаления всех полей, кроме последнего поля:
$ sed 's/.*,//' file

11
2
3
4
5

Это регулярное выражение удаляет все до последней запятой (.*,), что приводит к удалению всех полей, кроме последнего поля.

  1. Для печати только 1-го поля:
$ sed 's/,.*//' file

Solaris
Ubuntu
Fedora
LinuxMint
RedHat

Это регулярное выражение(,.*) удаляет символы, начиная с 1-й запятой до конца, в результате чего удаляются все поля, кроме последнего поля.

  1. Чтобы удалить 2-е поле:
$ sed 's/,[^,]*,/,/' file

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

Регулярное выражение (,[^,]*,) выполняет поиск запятой и последовательности символов, за которыми следует запятая, что приводит к совпадению со 2-м столбцом, и заменяет этот шаблон, соответствующий только запятой, в конечном итоге завершается удалением 2-го столбца.

Примечание: Удаление полей в середине становится более сложным в sed, так как каждое поле должно быть сопоставлено буквально.

  1. Для печати только 2-го поля:
$ sed 's/[^,]*,\([^,]*\).*/\1/' file

25
31
21
45
12

Регулярное выражение соответствует первому полю, второму полю и остальным, однако группирует только 2-е поле. Вся строка теперь заменена 2-м полем(\1), поэтому отображается только 2-е поле.

  1. Печатайте только строки, в которых последний столбец представляет собой однозначное число:
$ sed -n '/.*,[0-9]$/p' file

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

Регулярное выражение (,[0-9]$) проверяет наличие одной цифры в последнем поле, и команда p выводит строку, соответствующую этому условию.

  1. Чтобы пронумеровать все строки в файле:
$ 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

Это имитация команды cat-n. awk делает это легко, используя специальную переменную NR. Команда " = " sed задает номер строки каждой строки, за которой следует сама строка. Выходные данные sed передаются по конвейеру в другую команду sed для соединения каждые 2 строки.

  1. Замените последнее поле на 99, если 1 - е поле - "Ubuntu".:
$ sed 's/\(Ubuntu\)\(,.*,\).*/\1\299/' file

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

Это регулярное выражение соответствует "Ubuntu" и до конца, за исключением последнего столбца, а также группирует каждый из них. В заменяемой детали заменяются 1-я и 2-я группы вместе с новым номером 99.

  1. Удалите 2-е поле, если 1-е поле "RedHat":
$ sed 's/\(RedHat,\)[^,]*\(.*\)/\1\2/' file

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

1-е поле "RedHat", 2-е поле и остальные поля сгруппированы, и замена выполняется только 1-й и последней группой , что приводит к удалению 2-го поля.

  1. Чтобы вставить новый столбец в конце(последний столбец) :
$ sed 's/.*/&,A/' file

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

Регулярное выражение (.*) соответствует всей строке и заменяет ее самой строкой (&) и новым полем.

  1. Чтобы вставить новый столбец в начало(1 - й столбец):
$ sed 's/.*/A,&/' file

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

Как и в предыдущем примере, только за совпадающей строкой следует новый столбец

Я надеюсь, что это поможет. Дайте мне знать, если вам нужно использовать Awk или любую другую команду. Спасибо

2021-11-24 07:36:29

спасибо за подробное объяснение, но, к сожалению, это не решает проблему.
Helium

На других языках

Эта страница на других языках

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