Видаліть кілька рядків у файлі CSV


1

Я працюю над цим завданням, щоб видалити рядки з файлу CSV з різними клієнтами. Я зрозумів, як видалити одного конкретного клієнта за допомогою цього коду:

delete() {
  awk -F "\"*;\"*" '$1 != '$@' {print $ALL}' input.csv > output.csv
}

delete $@

Однак тепер мені доводиться видаляти декількох клієнтів одночасно. Я можу визначити клієнта за номером клієнта, який зберігається в першій колонці файлу csv. Я повинен створити масив для різних номерів клієнтів і створити цикл, який проходить час, щоб пройти через масив, але я не можу це зрозуміти.


1
Що таке $ALL? Чим print $ALLвідрізняється від print? І чому ти передаєш $@поодинокі цитати? Це означає, що вона не буде розширена.
тердон

@terdon, здається, що одиничні котирування закриваються раніше, а потім знову відкриваються після $@(все-таки це не найкращий спосіб передавати параметри оболонки для
пробудження

@steeldriver ах, так, дякую. Все-таки, як ви кажете ...
terdon

Відповіді:


2

Я не впевнений, чому ви вкладаєте це в функцію оболонки - я вважаю, що це вимога вашого завдання.

По-перше, зауважте, що використання "*;"*в якості роздільника поля в Awk не є надійним способом обробки цитованих полів CSV - воно не вдасться, наприклад, якщо буде вказано або перше поле, або останнє поле у ​​рядку, і це не збереже цитовані роздільники ( тобто цитовані поля, які насправді містять а ;), що пропускає всю точку цитування полів CSV.

По-друге, не слід намагатися таким чином передавати змінні оболонки (або позиційні параметри) у вираз Awk - правильний спосіб - або експортувати їх, а потім отримувати доступ до них через ENVIRONмасив, або використовувати параметр командного рядка -v. Тож краще буде написана ваша реалізація "єдиного замовника"

delcust() {
  awk -F '"*;"*' -v cust="$1" '$1 != cust' input.csv > output.csv
}
delcust "$1"

Хоча ви можете змінити це для передачі декількох позиційних параметрів, я б запропонував передати список клієнтів за допомогою стандартного введення та проаналізувати його як файл значень; таким чином ви можете зробити канонічний пошук Awk на основі асоціативного масиву (або хеша):

delcusts() {
  printf '%s\n' "$@" | awk -F'"*;"*' 'NR==FNR {custs[$0]=1; next} !($1 in custs)' - input.csv > output.csv
}
delcusts "$@"

Зауважте, що явний параметр printу Awk не потрібен, оскільки printце дія за замовчуванням, якщо правило оцінює не нульове значення.


1
... тоді прийміть відповідь. :)
Крила

0

Немає потреби в масиві. Ви можете визначити свою функцію так:

delete() {
  awk -v customer="^($1)\$" -F ";" '$1 !~ customer {print $ALL}' input.csv >output.csv 
}

Я не розумів, як ви визначили роздільник поля, тому змінив його, щоб мати можливість протестувати. Відповідна частина полягає у використанні заперечного регулярного виразу !~. Також я використав -vпараметр для awk, який може врятувати вас від безлічі оболонок, котируючи головний біль.

За допомогою цього параметра можна використовувати такий параметр, щоб видалити декілька клієнтів:

delete 'bla|foo'

Для input.csv, як це:

bla;blu;bli
foo;faa;fii
blafoo;blufaa;blifii

це дасть урожай

blafoo;blufaa;blifii

у виході.csv.

Якщо ви дійсно хочете використовувати масив, ви можете додатково визначити невелику помічну функцію, яка готує масив до використання з delete()функцією, наведеною вище:

join() { local IFS=\|; echo "$*"; }

За допомогою цього ви зможете визначити масив bash і перетворити його в альтернативний синтаксис регулярного вирівнювання:

$ a=(bla blu)
$ join ${a[@]}
bla|blu

Тоді ви можете зателефонувати delete()так:

$ a=(customer1 customer2)
$ delete "$(join ${a[@]})"

(Маленька бічна примітка для користувачів zsh: join()функція не потрібна для zsh, ви можете просто використовувати таке розширення параметрів: ${(j:|:)a}щоб з'єднати всі елементи масиву з |символом)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.