【问题标题】:creating a JSON object in bash using output from a command to standout使用命令的输出在 bash 中创建 JSON 对象以脱颖而出
【发布时间】:2021-07-30 05:05:20
【问题描述】:

在 shell 脚本中,我试图动态地从命令中获取输出并将其转换为 json 对象。值得庆幸的是,输出被分离成一种应该使它变得容易的格式,因为输出实际上是“key”:“value”。这应该很容易,对吧?但是我一直在摆弄和阅读这么多线程,现在我的头在旋转,我不知道现在该采取什么路线。

我似乎不知道如何在将在 vanilla macOS 主机上运行的 bash 脚本(它需要是一个 shell 脚本)中执行此操作(不使用 brew 或编译添加命令)。

我正在尝试使用类似“diskutil info /dev/disk2”的命令,输出数据如下:

   Device Identifier:         disk2
   Device Node:               /dev/disk2
   Whole:                     Yes
   Part of Whole:             disk2
   Device / Media Name:       DataTraveler G3

   Volume Name:               Not applicable (no file system)
   Mounted:                   Not applicable (no file system)
   File System:               None

   Content (IOContent):       GUID_partition_scheme
   OS Can Be Installed:       No
   Media Type:                Generic
   Protocol:                  USB
   SMART Status:              Not Supported

   Disk Size:                 31.0 GB (30967529472 Bytes) (exactly 60483456 512-Byte-Units)
   Device Block Size:         512 Bytes

   Read-Only Media:           No
   Read-Only Volume:          Not applicable (no file system)

   Device Location:           External
   Removable Media:           Removable
   Media Removal:             Software-Activated

   Virtual:                   No

并获得一个(有效的)JSON 对象,如下所示:

{   "Device Identifier" : "disk2",
    "Device Node" : "/dev/disk2",
    "Whole" : "Yes",
    "Part of Whole" : "disk2",
    "Device / Media Name" : "DataTraveler G3",
    "Volume Name" : "Not applicable (no file system)",
    "Mounted" : "Not applicable (no file system)",
    "File System" : "None",
    "Content (IOContent)" : "GUID_partition_scheme",
    "OS Can Be Installed" : "No",
    "Media Type" : "Generic",
    "Protocol" : "USB",
    "SMART Status" : "Not Supported",
    "Disk Size" : "31.0 GB (30967529472 Bytes) (exactly 60483456 512-Byte-Units)",
    "Device Block Size" : "512 Bytes",
    "Read-Only Media" : "No",
    "Read-Only Volume" : "Not applicable (no file system)",
    "Device Location" : "External",
    "Removable Media" : "Removable",
    "Media Removal" : "Software-Activated",
    "Virtual" : "No"
}

现在,是的,我可以使用 awk 或其他东西手动构建它以查找“设备标识符:”并将 $0 分配给该值。但是这个 diskutil 命令的结果是动态的,并且会根据设备文件中的存储类型(/dev/disk2 vs /dev/disk3 vs /dev/disk4 等)而变化。这样就结束了。

命令 diskutil 有一个创建 -plist 的选项,macOS 有一个名为 plutil 的实用程序,可以将 plist 转换为 json... ?除了 1) plist 必须写入文件,因为 plutil 只从文件中读取,以及 2) json 对象的格式无效。什么都不感谢。这样就结束了。

我可以使用 sed 来抓取冒号之前的字符串,并将它们放在一个变量中作为键。我可以使用 sed 来抓取冒号后面的字符串并将它们放入变量中以获得值。但后来我无法理解如何将它们拉链成对。我想知道我是否可以创建一个数组并即时创建键/值对,一次一个,但我似乎也无法弄清楚。

也许我累了,或者这是不可能的(或者使用香草命令非常困难),但我会很感激任何人的指导,这可能会帮助我向前推进一点。 谢谢!

(旁注:我突然想到在这台 macOS 主机上查看“apropos json”,因此作为一个选项深入研究......呸,死路一条。)

【问题讨论】:

  • 为什么它“需要是一个 bash 脚本”?一个普通的 MacOS 主机具有开箱即用的 Python。事实上,即使您要加倍考虑需要成为 bash 脚本,最好的答案还是一个 bash 脚本启动 Python,因为 Python 有 JSON 库而 bash 没有't。

标签: json bash macos


【解决方案1】:

如果使用连字符,plutil 可以从标准输入读取并写入标准输出。 jq 可以使用以下 Json。

diskutil info -plist /dev/disk1 | plutil -convert json -r -o - -

【讨论】:

    【解决方案2】:
    #!/bin/perl
    # ./r.pl < diskutil info /dev/disk2'
    my $w='{' . "\n";
    while(<>) {
    chomp;
    my $a = $_;
    my ($m1,$m2) = split(':',$_);
    if($m1) {
    $m1 =~ s/^\s+//;
    $m2 =~ s/^\s+//;
    $cm = eof() ? '':',';
    $w .= '"' . $m1 . '":"' . $m2 . '"' . $cm . "\n";
    }
    }
    print $w . '}';
    

    【讨论】:

    • 当您的卷名中包含引号并且需要从包含" 更改为\" 时会发生什么?还是需要加倍才能成为有效 JSON 的反斜杠,或者需要更改为 \t 的制表符,或者需要更改为 \u#### 转义的不可打印字符?
    • (另外,./r.pl &lt; diskutil info /dev/disk2./r.pl info /dev/disk2 &lt;diskutil 相同;它diskutil info /dev/disk2 | ./r.pl 相同)
    【解决方案3】:

    使用提供 JSON 库的语言(例如 Python,包含在现有的 MacOS 中)。您在这里的示例数据没有做任何简单的字符串操作技术无法处理的事情,但是一旦您的卷标名称中包含引号或反斜杠,简单的答案就会严重破坏;相比之下,以下将始终发出有效的 JSON 作为其输出。

    #!/bin/sh
    diskutil info /dev/disk2 | python -c '
    import re, sys, json
    
    content_re = re.compile(r"^\s*(\S[^:]+):\s*(\S(?:.*\S)?)\s*$")
    content = {}
    
    for line in sys.stdin:
        match = content_re.match(line)
        if match == None:
            continue
        content[match.group(1)] = match.group(2)
    json.dump(content, sys.stdout, indent=4)
    '
    

    ...正确地作为输出发出(给定 diskutil 发出您声明的输入):

    {
        "Device Identifier": "disk2",
        "Device Node": "/dev/disk2",
        "Whole": "Yes",
        "Part of Whole": "disk2",
        "Device / Media Name": "DataTraveler G3",
        "Volume Name": "Not applicable (no file system)",
        "Mounted": "Not applicable (no file system)",
        "File System": "None",
        "Content (IOContent)": "GUID_partition_scheme",
        "OS Can Be Installed": "No",
        "Media Type": "Generic",
        "Protocol": "USB",
        "SMART Status": "Not Supported",
        "Disk Size": "31.0 GB (30967529472 Bytes) (exactly 60483456 512-Byte-Units)",
        "Device Block Size": "512 Bytes",
        "Read-Only Media": "No",
        "Read-Only Volume": "Not applicable (no file system)",
        "Device Location": "External",
        "Removable Media": "Removable",
        "Media Removal": "Software-Activated",
        "Virtual": "No"
    }
    

    【讨论】:

    • 当值为单个字符时,正则表达式将不匹配。
    • 显然,是的。在编写它时,我认为这比在行尾捕获空格作为数据的一部分更好。也就是说,它已被修改以解决此案。
    • ...:\s*(.*?)\s*$更简单吗?
    • 这当然也是一种合理的做法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多