使用BASH将CSV转换为JSON

dex*_*now 2 csv bash shell json

我试图将下面的csv转换成json格式.

Africa,Kenya,NAI,281
Africa,Kenya,NAI,281
Asia,India,NSI,100
Asia,India,BSE,160
Asia,Pakistan,ISE,100
Asia,Pakistan,ANO,100
European Union,United Kingdom,LSE,100
Run Code Online (Sandbox Code Playgroud)

这是所需的json格式,我无法创建它.我将在此下面发布我的工作..任何帮助或方向将不胜感激...

  {"name":"Africa",
      "children":[
      {"name":"Kenya",
          "children":[
          {"name":"NAI","size":"109"},
          {"name":"NAA","size":"160"}]}]},
  {"name":"Asia",
      "children":[
      {"name":"India",
          "children":[
          {"name":"NSI","size":"100"},
          {"name":"BSE","size":"60"}]},
  {"name":"Pakistan",
      "children":[
      {"name":"ISE","size":"120"},
      {"name":"ANO","size":"433"}]}]},
  {"name":"European Union",
        "children":[
        {"name":"United Kingdom",
            "children":[
            {"name":"LSE","size":"550"},
            {"name":"PLU","size":"123"}]}]}
Run Code Online (Sandbox Code Playgroud)

工作正在进行中.

$ 1是上面粘贴的csv值的文件.

#!/bin/bash

pcountry=$(head -1 $1 | cut -d, -f2)

cat $1 | while read line ; do 

region=$(echo $line|cut -d, -f1)
country=$(echo $line|cut -d, -f2)
code=$(echo $line|cut -d, -f3-)
size=$(echo $line|cut -d, -f4)

if test "$pcountry" == "$country" ;
  then 
  echo -e {\"name\":\"$region\", '\n' \"children\": [ '\n'{\"name\":\"$country\",'\n'\"children\": [ '\n' \{\"name\":\"NAI\",\"size\":\"$size\"\}
  else
      if test "$pregion" == "$region"
      then :
      else 
          echo -e ,'\n'{\"name\":\""$region\", '\n' \"children\": [ '\n'{\"name\":\"$country\",'\n'\"children\": [ '\n' \{\"name\":\"NAI\",\"size\":\"$size\"\},


pcountry=$country
pregion=$region

fi ; done
Run Code Online (Sandbox Code Playgroud)

问题是我似乎无法找到一个国家价值何时结束的方法.

Dav*_*ley 5

正如许多评论者所说,使用shell进行这种转换是一个可怕的想法。而且,仅用bash内建函数几乎不可能做到这一点。和外壳脚本用于标准UNIX命令等结合sedawkcut等反正。您应该选择一种为此类迭代解析/处理而构建的更好的语言,以解决您的问题。

但是,由于太晚了,我喝了太多咖啡,所以我整理了一个bash脚本(并添加了一些sed语法分析帮助),该脚本将获取.csv您拥有的示例数据并以您指定的格式输出JSON。这是脚本:

#! /bin/bash 
# Initial input file format:
#
#         Africa,Kenya,NAI,281
#         Africa,Kenya,NAA,281
#         Asia,India,NSI,100
#         Asia,India,BSE,160
#         Asia,Pakistan,ISE,100
#         Asia,Pakistan,ANO,100
#         European Union,United Kingdom,LSE,100
#
# Intermediate file format for parsing to JSON:
#
#         Africa|Kenya:NAI=281
#         Asia|India:BSE=160&NSI=100|Pakistan:ISE=100&ANO=100
#         European Union|United Kingdom:LSE=100
#
# Call as:
#
#   $ ./script INPUTFILE.csv >OUTPUTFILE.json
#


# temporary files for output/parsing
TMP="./tmp.dat"
TMP2="./tmp2.dat"
>$TMP
>$TMP2

# read through initial file and output intermediate format
while read line
do
    region=$(echo $line | cut -d, -f1)
    country=$(echo $line | cut -d, -f2)
    code=$(echo $line | cut -d, -f3)
    size=$(echo $line | cut -d, -f4)

    # region record already started
    if grep "^$region" $TMP 2>&1 >/dev/null ;then
        >$TMP2 
        while read rec
        do
            if echo $rec | grep "^$region" 2>&1 >/dev/null
            then
                if echo "$rec" | grep "\|$country:" 2>&1 >/dev/null
                then
                    echo "$rec" | sed -e 's/\('"$country"':[^\|][^\|]*\)/\1\&'"$code"'='"$size"'/' >>$TMP2
                else
                    echo "$rec|$country:$code=$size" >>$TMP2
                fi
            else
                echo $rec >>$TMP2
            fi
        done < $TMP
        mv $TMP2 $TMP
    else
    # new region
        echo "$region|$country:$code=$size" >>$TMP
    fi

done < $1

# Parse through our intermediary format and output JSON to standard out
echo "["
country_count=$(cat $TMP | wc -l)
while read line
do
    country=$(echo $line | cut -d\| -f1)
    echo "{ \"name\": \"$country\", "
    echo "  \"children\": ["
    region_count=$(echo $line | cut -d\| -f2- | sed -e 's/|/\n/g' | wc -l)
    echo $line | cut -d\| -f2- | sed -e 's/|/\n/g' | 
    while read region
    do
        name=$(echo $region | cut -d: -f1)
        echo "    { \"name\": \"$name\", "
        echo "      \"children\": ["
            code_count=$(echo $region | sed -e 's/^'"$name"'://' -e 's/&/\n/g'  | wc -l)
            echo $region | sed -e 's/^'"$name"'://' -e 's/&/\n/g'  |
            while read code_size
            do
                code=$(echo $code_size | cut -d= -f1)
                size=$(echo $code_size | cut -d= -f2)
                code_count=$((code_count - 1))
                COMMA=""
                if [ $code_count -gt 0 ]; then
                  COMMA=","
                fi
                echo "        { \"name\": \"$code\", \"size\": \"$size\" }$COMMA " 
            done
        echo "      ]"
        region_count=$((region_count - 1))
        if [ $region_count -gt 0 ]; then
            echo "    },"
        else
            echo "    }"
        fi
    done 
    echo "  ]"
    country_count=$((country_count - 1))
    COMMA=""
    if [ $country_count -gt 0 ]; then
        COMMA=","
    fi    
    echo "}$COMMA"

done < $TMP
echo "]"

exit 0
Run Code Online (Sandbox Code Playgroud)

并且,这是上面脚本的结果输出:

[
{ "name": "Africa",
  "children": [
    { "name": "Kenya",
      "children": [
        { "name": "NAI", "size": "281" },
        { "name": "NAA", "size": "281" }
      ]
    }
  ]
},
{ "name": "Asia",
  "children": [
    { "name": "India",
      "children": [
        { "name": "NSI", "size": "100" },
        { "name": "BSE", "size": "160" }
      ]
    },
    { "name": "Pakistan",
      "children": [
        { "name": "ISE", "size": "100" },
        { "name": "ANO", "size": "100" }
      ]
    }
  ]
},
{ "name": "European Union",
  "children": [
    { "name": "United Kingdom",
      "children": [
        { "name": "LSE", "size": "100" }
      ]
    }
  ]
}
]
Run Code Online (Sandbox Code Playgroud)

请不要在任何生产环境中使用上述代码。

  • 我觉得应该有一个鼓励不良行为的徽章 (2认同)

jq1*_*727 5

这是使用jq的解决方案.

如果filter.jq包含以下过滤器

 reduce (
     split("\n")[]                  # split string into lines
   | split(",")                     # split data
   | select(length>0)               # eliminate blanks
 )  as [$c1,$c2,$c3,$c4] (          # convert to object 
     {}                             #   e.g. "Africa": { "Kenya": {  
   ; setpath([$c1,$c2,"name"];$c3)  #           "name": "NAI",
   | setpath([$c1,$c2,"size"];$c4)  #           "size": "281"        
)                                   #        }, }
| [                                 # then build final array of objects format:
    keys[] as $k1                   # [ {                                               
  | {name: $k1, children: (         #   "name": "Africa",                                  
       .[$k1]                       #   "children": {                                   
     | keys[] as $k2                #     "name": "Kenya",                                 
     | {name: $k2, children:.[$k2]} #     "children": { "name": "NAI", "size": "281" }
    )}                              #   ...
  ]
Run Code Online (Sandbox Code Playgroud)

data包含示例数据然后命令

$ jq -M -Rsr -f filter.jq data
Run Code Online (Sandbox Code Playgroud)

产生

[
  {
    "name": "Africa",
    "children": {
      "name": "Kenya",
      "children": {
        "name": "NAI",
        "size": "281"
      }
    }
  },
  {
    "name": "Asia",
    "children": {
      "name": "India",
      "children": {
        "name": "BSE",
        "size": "160"
      }
    }
  },
  {
    "name": "Asia",
    "children": {
      "name": "Pakistan",
      "children": {
        "name": "ANO",
        "size": "100"
      }
    }
  },
  {
    "name": "European Union",
    "children": {
      "name": "United Kingdom",
      "children": {
        "name": "LSE",
        "size": "100"
      }
    }
  }
]
Run Code Online (Sandbox Code Playgroud)