从Bash脚本输出JSON

54

我有一个输出服务器详情的 bash 脚本。问题是我需要将输出格式转换为 JSON。最佳方法是什么?以下是 bash 脚本代码:

# Get hostname
hostname=`hostname -A` 2> /dev/null

# Get distro
distro=`python -c 'import platform ; print platform.linux_distribution()[0] + " " +        platform.linux_distribution()[1]'` 2> /dev/null

# Get uptime
if [ -f "/proc/uptime" ]; then
uptime=`cat /proc/uptime`
uptime=${uptime%%.*}
seconds=$(( uptime%60 ))
minutes=$(( uptime/60%60 ))
hours=$(( uptime/60/60%24 ))
days=$(( uptime/60/60/24 ))
uptime="$days days, $hours hours, $minutes minutes, $seconds seconds"
else
uptime=""
fi

echo $hostname
echo $distro
echo $uptime

所以我想要的输出结果是:

{"hostname":"server.domain.com", "distro":"CentOS 6.3", "uptime":"5 days, 22 hours, 1 minutes, 41 seconds"}

谢谢。


7
你不想完全使用Python吗?我的意思是,你已经在使用它了,Python可以轻松确定主机名、读取正常运行时间,并生成你想要的JSON,这有什么问题吗? - willglynn
请参考这个更新的问题,其中有更好的答案。请注意,构建有效的JSON需要正确处理输入中的特殊字符,如"\n,简单的字符串连接并不能保证这一点。 - jakub.g
7个回答

106

如果你只需要输出一个小的JSON,可以使用printf

printf '{"hostname":"%s","distro":"%s","uptime":"%s"}\n' "$hostname" "$distro" "$uptime"

如果需要生成较大的JSON,请使用heredoc,如 leandro-mora 所解释的那样。如果您使用这种 heredoc 解决方案,请确保给他的答案 点赞

cat <<EOF > /your/path/myjson.json
{"id" : "$my_id"}
EOF
一些较新的发行版有一个名为/etc/lsb-release或类似名称的文件(cat /etc/*release)。因此,您可能可以摆脱对Python的依赖:
distro=$(awk -F= 'END { print $2 }' /etc/lsb-release)

顺便说一下,你可能应该不再使用反引号(backticks),它们有点过时了。


16
使用 printf 会更加简洁:printf '{"hostname":"%s","distro":"%s","uptime":"%s"}\n' "$hostname" "$distro" "$uptime" - glenn jackman
对于printf点赞,因为它似乎在BSD上也能工作。至少在OSX中,从手册中我看不到' -e '选项的echo命令。 - Michel Müller
1
点赞评论中提到反引号。它们不仅过时,而且难以阅读,最重要的是无法嵌套。嵌套命令替换是一种偶尔使用但非常强大的机制。 - Graham Nicholls
一般不应使用echo -e。POSIX sh规范不允许它(或者说,只允许它在标准输出中打印“-e”而没有其他行为——与大多数bashisms不同,这个不仅扩展了标准,而且还主动破坏了它)。bash根据活动运行时配置可能会或可能不会支持它;请参见为什么printfecho更好?上的[unix.se],和/或POSIX echo规范,后者建议使用printf代替! - Charles Duffy
@CharlesDuffy:同意。我已经删除了echo -e的解决方案。从来没有想过这个答案会引起太多关注...或者说我的任何答案都不会... - Steve

58

我觉得使用 cat 更容易创建 JSON:

cat <<EOF > /your/path/myjson.json
{"id" : "$my_id"}
EOF

4
如果不需要进行 JSON 编码,那么这就是一个完整的解决方案。 - Mark Stosberg

4
我并不是个bash高手,但我写了一个对我来说完美运行的解决方案。因此,我决定与社区分享
首先,我创建了一个名为json.sh的bash脚本。
arr=();

while read x y; 
do 
    arr=("${arr[@]}" $x $y)
done

vars=(${arr[@]})
len=${#arr[@]}

printf "{"
for (( i=0; i<len; i+=2 ))
do
    printf "\"${vars[i]}\": ${vars[i+1]}"
    if [ $i -lt $((len-2)) ] ; then
        printf ", "
    fi
done
printf "}"
echo

现在我可以轻松地执行它:

$ echo key1 1 key2 2 key3 3 | ./json.sh
{"key1":1, "key2":2, "key3":3}

1

@Jimilian的脚本对我非常有帮助。我稍微修改了一下,以便将数据发送到zabbix自动发现

arr=()

while read x y;
do
    arr=("${arr[@]}" $x $y)
done

vars=(${arr[@]})
len=${#arr[@]}

printf "{\n"
printf "\t"data":[\n"

for (( i=0; i<len; i+=2 ))
do
     printf "\t{  "{#VAL1}":\"${vars[i]}\",\t"{#VAL2}":\"${vars[i+1]}\"  }"

    if [ $i -lt $((len-2)) ] ; then
        printf ",\n"
    fi
done
printf "\n"
printf "\t]\n"
printf "}\n"
echo

输出:

    $ echo "A 1 B 2 C 3 D 4 E 5" | ./testjson.sh
{
    data:[
    {  {#VAL1}:"A", {#VAL2}:"1"  },
    {  {#VAL1}:"B", {#VAL2}:"2"  },
    {  {#VAL1}:"C", {#VAL2}:"3"  },
    {  {#VAL1}:"D", {#VAL2}:"4"  },
    {  {#VAL1}:"E", {#VAL2}:"5"  }
    ]
}

1

我用 Go 写了一个小程序,json_encode。对于这样的情况,它的表现非常好:

$ ./getDistro.sh | json_encode
["my.dev","Ubuntu 17.10","4 days, 2 hours, 21 minutes, 17 seconds"]

1
data=$(echo  " BUILD_NUMBER : ${BUILD_NUMBER} , BUILD_ID : ${BUILD_ID} , JOB_NAME : ${JOB_NAME} " | sed 's/ /"/g')

output => data="BUILD_NUMBER":"29","BUILD_ID":"29","JOB_NAME":"OSM_LOG_ANA"

2
欢迎来到 Stack Overflow。除了仅仅发布代码,更详细的解释会更有益。试着解释一下问题代码中的错误,并说明你的代码如何修复它。 - sampathsris

0
回答主题,如果您需要从任何命令行获取一个以制表符分隔的输出,并且需要将其格式化为JSON列表的列表,则可以执行以下操作:
echo <tsv_string> | python3 -c "import sys,json; print(json.dumps([row.split('\t') for row in sys.stdin.read().splitlines() if True]))

例如,要将zfs列表输出为json:
zfs list -Hpr -o name,creation,mountpoint,mounted | python3 -c "import sys,json; print(json.dumps([row.split('\t') for row in sys.stdin.read().splitlines() if True]))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接