Добавление данных, если элементы из двух разных строк в двух разных файлах CSV совпадают, с использованием сценариев Bash

Я новичок в написании сценариев Bash и все еще на стадии обучения. В настоящее время я работаю над файлами CSV, и существует требование: если две строки совпадают в двух разных файлах CSV, следует добавить столбец данных.

Например,

csv_file1.csv:

Account,Entity,Data
A1101,E101,1000

csv_file2.csv:

Account,Entity,Data
A1101,E101,2000
A1101,E102,2000

При выполнении сценария bash ожидаемым результатом должен быть новый файл CSV со строками:

Account,Entity,Data
A1101,E101,3000
A1101,E102,2000

Поскольку элементы в 1-й строке «csv_file1» и элементы в 1-й строке «csv_file2» совпадают, данные в столбце «Данные» должны быть добавлены.

Ниже приведен код, который я попытался распечатать содержимое обоих файлов CSV. Но я не могу двигаться вперед отсюда.

#!/bin/bash
#assigns the CSV file to a variable
FILE1 = "csv_file1.csv"

#reads each line and splits it into three variables

while IFS=, read -r Account Entity Data; do

echo "$Account#""$Entity#""$Data"

done < <(tail -n +2 $FILE1)

#assigns the CSV file to a variable
FILE2 = "csv_file2.csv"

#reads each line and splits it into three variables

while IFS=, read -r Account Entity Data; do

echo "$Account#""$Entity#""$Data"

done < <(tail -n +2 $FILE2)

Было бы здорово, если бы вы, ребята, помогли мне со своими идеями. пожалуйста, дайте мне знать, если потребуется какая-либо дополнительная информация. Заранее большое спасибо!

Почему бы не использовать awk? ` -k --csv Включить специальную обработку для файлов со значениями, разделенными запятыми (CSV). См. раздел «Работа с файлами значений, разделенных запятыми». Эту опцию нельзя использовать с --posix. Попытка сделать это приводит к фатальной ошибке. ` -F fs --field-separator fs

James 07.05.2024 07:13

С помощью PHP я создаю массив с учетной записью и сущностью в качестве индекса. Если ключ массива не существует, создайте его, иначе добавьте значение.

Wolfgang Blessen 07.05.2024 08:00
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

== файл: chk.awk ==

@load "filefuncs"
{
  #// input: file=xxx.csv

  #// check if $file defined
  if (file= = "") {
    printf ("Please use `-v file=xxx.csv` to define a csv file\n");
    exit 2;
  }

  #// check if inout file exist
  ret=stat(file,fdata)
  if (ret<0) {
    printf ("File '%s' seems not exist !?\n", file);
    exit 3;
  }

  printf ("* NR=%d\n", NR);

  printf ("1st file:'%s'\n", $0);
  #// Read from 2nd file
  ret = getline sec < file
  if (ret)
    printf ("2nd file:'%s'\n", sec);
  else
    printf ("2nd file already EOF\n");
    #//? Terminated ? 
} END {
  #// 1st file END, but how about 2nd?
  if (ret) {
    printf ("2nd left: --\n");
    while (ret) {
      ret = getline sec < file
      if (ret) printf ("2nd: '%s'\n", sec );
    }
  }
}

командная строка:

$ gawk -k  -v file=2.csv -f chk.awk 1.csv
* NR=1
1st file:'Account,Entity,Data'
2nd file:'Account,Entity,Data'
* NR=2
1st file:'A1101,E101,1000'
2nd file:'A1101,E101,2000'
2nd left: --
2nd: 'A1101,E102,2000'
2nd: 'A1101,E102,2000'
2nd: 'A1101,E102,2000'
2nd: 'A1101,E102,2000'

или

$ gawk -k  -v file=1.csv -f chk.awk 2.csv
* NR=1
1st file:'Account,Entity,Data'
2nd file:'Account,Entity,Data'
* NR=2
1st file:'A1101,E101,2000'
2nd file:'A1101,E101,1000'
* NR=3
1st file:'A1101,E102,2000'
2nd file already EOF
* NR=4
1st file:'A1101,E102,2000'
2nd file already EOF
* NR=5
1st file:'A1101,E102,2000'
2nd file already EOF
* NR=6
1st file:'A1101,E102,2000'
2nd file already EOF

Теперь вы можете сами сравнить разницу между этими двумя файлами.

Ответ принят как подходящий

В вашем скрипте bash отсутствует сравнение двух входных данных и сумма при совпадении строк.

Если вы абсолютно хотите сделать это всего лишь с помощью bash, если ваши файлы CSV такие же простые, как то, что вы показываете (без полей в кавычках, без новых строк в полях...), если вы хотите также печатать строки, которые встречаются только в одном из два входных файла, и если вас не волнует окончательный порядок, вы можете попробовать:

#!/bin/bash

declare -iA a=()
declare -i Data
declare Account Entity

FILE1 = "csv_file1.csv"
FILE2 = "csv_file2.csv"

head -n1 "$FILE1"

while IFS=, read -r Account Entity Data; do
  a["$Account,$Entity"]+=Data
done < <(tail -n +2 "$FILE1"; tail -n +2 "$FILE2")

for k in "${!a[@]}"; do
  printf '%s,%d\n' "$k" ${a[$k]}
done

По соображениям производительности awk будет лучшим выбором, чем bash. С теми же предположениями, что и для версии bash, вы можете попробовать следующее с любым awk:

awk '
BEGIN { FS = OFS = "," }
NR == 1 { print; next }
FNR == 1 { next }
{ a[$1 OFS $2] += $3 }
END { for(k in a) print k, a[k] }
' csv_file1.csv csv_file2.csv

Мы объявляем запятую разделителем входных (FS) и выходных (OFS) полей. Мы печатаем строку заголовка первого файла и пропускаем строку заголовка второго файла.

При анализе файлов мы сохраняем содержимое в массиве a с двумя первыми полями в качестве ключа и последним полем в качестве значения (например, a[A1101,E101] = 1000), добавляя третье поле к значению, если ключ уже существует.

В конце (END) мы печатаем содержимое массива a.

Если ваш формат CSV более сложный, с полями в двойных кавычках, возможно, содержащим экранированные двойные кавычки ("") или даже символы новой строки, вы можете попробовать опцию --csv в последних версиях GNU awk:

awk --csv '
function quote(s) {
  if (s ~ /[",\n]/) {
    gsub(/"/, "\"\"", s)
    s = "\"" s "\""
  }
  return s
}

BEGIN { OFS = "," }
NR == 1 { print; next }
FNR == 1 { next }
{ a[$1][$2] += $3 }
END { for(k1 in a) for(k2 in a[k1]) print quote(k1), quote(k2), a[k1][k2] }
' csv_file1.csv csv_file2.csv

Демо:

$ cat csv_file1.csv 
Account,Entity,Data
A1101,E101,1000
A1101,"E101,E102",3000
A1101,"hello, ""world""",1000

$ cat csv_file2.csv 
Account,Entity,Data
A1101,E101,2000
A1101,E102,2000
A1101,"E101,E102",2000
A1101,"hello, ""world""",4000

$ awk --csv -f foo2.awk csv_file*
Account,Entity,Data
A1101,"hello, ""world""",5000
A1101,E101,3000
A1101,E102,2000
A1101,"E101,E102",5000

Но тогда настоящий синтаксический анализатор CSV, вероятно, будет безопаснее.

Привет, Рено, сработало чудесно. Спасибо за помощь! Из любопытства, можно ли упорядочить данные, заключенные в двойные кавычки? как и в случае со столбцом Entity, если есть данные типа «E101, E102», код awk игнорирует эти двойные кавычки.

Ram Kashyap 07.05.2024 11:49

Это было бы возможно, но тогда вам придется анализировать более сложный формат CSV. Что делать, если в некоторых полях вы избежали двойных кавычек? Я не рекомендую анализировать сложный CSV без специального анализатора. Вы можете попробовать вариант --csv GNU awk, но даже он имеет ограничения. Или вы можете попробовать один из многочисленных наборов инструментов CSV (например, csvkit).

Renaud Pacalet 07.05.2024 12:05

Если ваш CSV более сложный, чем тот, который вы показываете в своем вопросе, я отредактировал свой ответ, чтобы показать, как использовать опцию --csv в последних версиях GNU awk.

Renaud Pacalet 07.05.2024 15:03

Другие вопросы по теме