IFS отделяет строку, например Hello,World,this,is, скучная строка

Я пытаюсь проанализировать файл .csv, и у меня есть некоторые проблемы с IFS. В файле есть такие строки:

"Hello","World","this","is, a boring","line"

Столбцы разделены запятой, поэтому я попытался взорвать строку с помощью этого кода:

IFS=, read -r -a tempArr <<< "$line"

Но я получаю этот вывод:

"Hello"
"World"
"this"
"is
a boring"
"line"

Я понимаю, почему, поэтому я попробовал некоторые другие команды, но не получил ожидаемого результата.

IFS=\",\"
IFS=\",
IFS=',\"'
IFS=,\"

Каждый раз третий элемент разделяется на 2 части. Как я могу использовать IFS для разделения строки на 5 частей?

"Hello"
"World" 
"this" 
"is, a boring" 
"line"

person l4mb3rt    schedule 30.04.2016    source источник
comment
IFS не предназначен для разбора CSV-файлов; он может обрабатывать только простое разделение, которое не зависит от того, заключен ли разделитель в кавычки или нет. Используйте язык, в котором есть доступный анализатор CSV.   -  person chepner    schedule 30.04.2016
comment
Я почти уверен, что это можно теоретически сделать в bash. И это не должно быть так сложно.   -  person Isiah Meadows    schedule 30.04.2016
comment
Не в чистом BASH, но, вероятно, в gnu awk   -  person anubhava    schedule 30.04.2016
comment
Разбор CSV сложен, особенно когда речь идет о полях со встроенными запятыми. Прислушайтесь к совету @chepner и используйте другие языки, такие как Python, Java, ..., чтобы правильно его проанализировать.   -  person Hai Vu    schedule 30.04.2016
comment
@Cyrus Это предполагает, что каждое поле заключено в кавычки, что не обязательно в файле CSV. hello,world,this,"is, a boring",line действителен.   -  person chepner    schedule 30.04.2016
comment
@impinball, да, это определенно можно сделать в bash - для этого есть библиотека. Но если вы прочитаете, что на самом деле находится в этой библиотеке, это на самом деле довольно сложно.   -  person Charles Duffy    schedule 30.04.2016
comment
см. github.com/lhunath/scripts/blob/. bashlib реализация парсинга CSV   -  person Charles Duffy    schedule 30.04.2016
comment
@CharlesDuffy Разбирать сложно, и точка. Bash, язык оболочки, основанный на подстановке, с большим количеством потоков, с плохой поддержкой низкоуровневых манипуляций с текстом, еще больше усложняет ситуацию. И да, я написал несколько парсеров даже для более сложных задач. Хотя я обычно использую JS.   -  person Isiah Meadows    schedule 30.04.2016


Ответы (3)


попробуйте это:

sed 's/","/"\n"/g' <<<"${line}"

sed имеет команду поиска и замены s, которая использует регулярное выражение для поиска по шаблону.

Регулярное выражение заменяет , в "," новой строкой char.

Как следствие, каждый элемент находится на отдельной строке.

person Jay jargot    schedule 30.04.2016

Вы можете использовать gawk с FPAT, чтобы определить, что делает строку действительной -

Ввод :

"привет", "мир", "это"

Сценарий :

gawk -n 'BEGIN{FS=",";OFS="\n";FPAT="([^,]+)|(\"[^\"]+\")"}{$1=$1;print $0}' somefile.csv

Вывод :

"привет"
"мир"
"это"

person sjsam    schedule 30.04.2016

bashlib предоставляет функцию csvline. Предполагая, что вы установили его где-то в своем PATH:

line='"Hello","World","this","is, a boring","line"'

source bashlib
csvline <<<"$line"
printf '%s\n' "${CSVLINE[@]}"

...вывод из приведенного выше:

Hello
World
this
is, a boring
line

Чтобы процитировать реализацию (авторское право lhunath), приведенный ниже текст взят из эта конкретная версия соответствующего репозитория git):

#  _______________________________________________________________________
# |__ csvline ____________________________________________________________|
#
#       csvline [-d delimiter] [-D line-delimiter]
#
# Parse a CSV record from standard input, storing the fields in the CSVLINE array.
#
# By default, a single line of input is read and parsed into comma-delimited fields.
# Fields can optionally contain double-quoted data, including field delimiters.
#
# A different field delimiter can be specified using -d.  You can use -D
# to change the definition of a "record" (eg. to support NULL-delimited records).
#
csvline() {
    CSVLINE=()
    local line field quoted=0 delimiter=, lineDelimiter=$'\n' c
    local OPTIND=1 arg
    while getopts :d: arg; do
        case $arg in
            d) delimiter=$OPTARG ;;
        esac
    done

    IFS= read -d "$lineDelimiter" -r line || return
    while IFS= read -rn1 c; do
        case $c in
            \")
                (( quoted = !quoted ))
                continue ;;
            $delimiter)
                if (( ! quoted )); then
                    CSVLINE+=( "$field" ) field=
                    continue
                fi ;;
        esac
        field+=$c
    done <<< "$line"
    [[ $field ]] && CSVLINE+=( "$field" ) ||:
} # _____________________________________________________________________
person Charles Duffy    schedule 30.04.2016