Проверьте, существует ли значение столбца 1 где-либо в столбце 2, запишите результат в столбец 3 (документ CSV) - PullRequest
0 голосов
/ 18 апреля 2019

Я ищу способ проверить, существует ли значение первого столбца (название продукта) где-либо во втором столбце (вариант продукта) и записать результат (совпадение / отсутствие совпадения) в третий столбец документа CSV.

Поскольку эта задача, вероятно, повторяется, я хотел бы начать работу над сценарием для этого.

Документ CSV в настоящее время имеет следующую структуру (третий столбец не содержит совпадений):

"PRODUCT";"VARIANT";"MATCH"
"100 DG";"black";""
"100 DG";"100 DG black";""
"100 DG";"silver 100 DG";""
"100 DG";"silver 100 DG US edition";""
...

Результат должен выглядеть следующим образом:

"PRODUCT";"VARIANT";"MATCH"
"100 DG";"black";"no"
"100 DG";"100 DG black";"yes"
"100 DG";"silver 100 DG";"yes"
"100 DG";"silver 100 DG US edition";"yes"
...

Итак, я хотел бы записать результаты в третий столбец:

  • если содержится, напишите 'yes'
  • если не содержится, напишите 'нет'

Я давно такого не делал и ценю вашу помощь.

Ответы [ 6 ]

1 голос
/ 18 апреля 2019

с Миллером (https://github.com/johnkerl/miller) -

mlr --csv --fs ";" put -S 'if ($VARIANT=~$PRODUCT) 
{$MATCH="yes"} 
    else 
{$MATCH="no"}' input_01.csv

или в одну строку

mlr --csv --fs ";" put -S 'if ($VARIANT=~$PRODUCT) {$MATCH="yes"} else {$MATCH="no"}' input_01.csv
1 голос
/ 18 апреля 2019

Это задание для awk

#! /bin/sh

FILE=$1

sed -e "s/\"//g" < $FILE | awk -F\; '{
        if (index($2,$1)) {
                result="yes"
        } else {
                result="no"
        }
        printf("\"%s\";\"%s\";\"%s\"\n", $1, $2, result)
}'

Сначала избавьтесь от всего \ "с помощью sed, отдых внутри awk прост. -F устанавливает разделитель полей на"; ", что имеет смыслдля файлов CSV. Затем воспользуйтесь индексом строковой функции AWK. Скрипты AWK, встроенные в скрипт Shell, очень мощные.

Использование:

shell$ bash report.sh data
"PRODUCT";"VARIANT";"no"
"100 DG";"black";"no"
"100 DG";"100 DG black";"yes"
"100 DG";"silver 100 DG";"yes"
"100 DG";"silver 100 DG US edition";"yes"

Мне нравится этот вариант, потому что a) он хорошо читаетсяи поддерживаемый код b) он избегает необходимости запуска нескольких других заданий Unix, так как все обрабатывается внутри этого одного процесса AWK (за исключением процессора и ввода-вывода)

1 голос
/ 18 апреля 2019

Попробуйте:

#!/bin/bash
echo "\"PRODUCT\";\"VARIANT\";\"MATCH\""
tail -n +2 $1 | while read CSVLINE ; do
    AMATCH=$(echo $CSVLINE | awk -F\; '{ print $1 }' | tr -d '"')
    BMATCH=$(echo $CSVLINE | awk -F\; '{ print $2 }')
    TESTGREP=$(echo $BMATCH | grep "$AMATCH")
    if [[ $TESTGREP ]] ; then
        echo "\""$AMATCH"\";"$BMATCH";\"yes\""
    else
        echo "\""$AMATCH"\";"$BMATCH";\"no\""
    fi
done

Пример (bash parse.sh file.csv):

root:~# bash parse.sh file.csv
"100 DG";"black";"no"
"100 DG";"100 DG black";"yes"
"100 DG";"silver 100 DG";"yes"
"100 DG";"silver 100 DG US edition";"yes"
root:~#
0 голосов
/ 21 апреля 2019

Использование awk:

$ awk -F\; '{                               # set field separator
    v=$1                                    # duplicate $1 for ...
    gsub(/^"|"$/,"",v)                      # ... removing quotes
    print $0 ";\"" ($2~v?"yes":"no") "\""   # print appending yes or no
}' file

Выход:

"PRODUCT";"VARIANT";"MATCH";"no"
"100 DG";"black";"";"no"
"100 DG";"100 DG black";"";"yes"
"100 DG";"silver 100 DG";"";"yes"
"100 DG";"silver 100 DG US edition";"";"yes"
0 голосов
/ 18 апреля 2019

Еще один однострочник Perl:

perl -i.back -ape '$.>1 && s/"(.+?)";"(?:(?!\1)[^"])*(\1)?.*?";"\K/${2}?"yes":"no"/e' file

Где:

-i.back     # replace file inplace but keep a backup with extension `.back`

Regex объяснение:

$.>1 &&     # if  line number greater than 1 (no change on first line)
s/          # substitute
  "(.+?)"   # capture the value of first column in group 1. 1 or more any character, not greedy
  ;"        # literally
  (?:       # start non capture group, tempered greedy token
    (?!\1)  # everything that is not the value contained in group 1 (i.e. the first column)
    [^"]    # 1 character that is not a double quote
  )*        # end group, may appear 0 or more time
  (\1)?     # group 2, same value as group 1, optional
  .*?       # 1 or more any character, not greedy
  ";"       # literally
  \K        # forget all we have seen until this position
/           # regex delim
  ${2}?     # does group 2 exist?
  "yes"     # it exits, change column 3 with "yes"
  :         # else
  "no"      # change column 3 with "yes"
/e          # end substitute, execute flag

Выход:

"PRODUCT";"VARIANT";"MATCH"
"100 DG";"black";"no"
"100 DG";"100 DG black";"yes"
"100 DG";"silver 100 DG";"yes"
"100 DG";"silver 100 DG US edition";"yes"
0 голосов
/ 18 апреля 2019

Вот прекрасный Perl для одной строки:

cat file.csv | perl -pe '/"(.*?)";"(.*?");""/; ($2 =~ /$1/) ? s/""/"yes"/ : s/""/"no"/'

Объяснение

/"(.*?)";"(.*?");""/; - это регулярное выражение, которое получает значения столбца 1 (в $1) и второго столбца (в $2)

($2 =~ /$1/) - это условие, которое означает, что «столбец 1 является подстрокой столбца 2»

Тогда у нас есть троичный оператор , который заменяет "" во входной строке на "yes", если условие выполнено, и "no" в противном случае.

Пример:

user@server:~$ cat file.csv | perl -pe '/"(.*?)";"(.*?");""/; ($2 =~ /$1/) ? s/""/"yes"/ : s/""/"no"/'
"PRODUCT";"VARIANT";"MATCH"
"100 DG";"black";"no"
"100 DG";"100 DG black";"yes"
"100 DG";"silver 100 DG";"yes"
"100 DG";"silver 100 DG US edition";"yes"
...