【问题标题】:Get a list of running VMs using VBoxManage使用 VBoxManage 获取正在运行的 VM 列表
【发布时间】:2023-03-18 19:46:01
【问题描述】:

我想遍历我正在运行的虚拟机并只返回引号之间的内容。

所以这个命令:

VBoxManage list runningvms

返回:

"UbuntuServer" {7ef01f8d-a7d5-4405-af42-94d85f999dff}

我只希望它返回:

UbuntuServer

这是我目前所拥有的(失败):

#!/bin/bash
for machine in `cat VBoxManage list runningvms`; do
        echo "$machine"
done
exit

【问题讨论】:

    标签: bash virtualbox


    【解决方案1】:

    警告:如果您的 VM 名称中包含 shell glob 字符或包含空格,那么所有这些都是有风险的。


    如果只有一个正在运行的 VM,您可以这样做:

    read machine stuff <<< $(VBoxManage list runningvms)
    echo "$machine"
    

    bash 数组的替代方案(相同条件):

    vbm=($(VBoxManage list runningvms))
    echo "${vbm[0]}"
    

    如果该程序返回多行,则更经典的方法是:

    for machine in $(VBoxManage list runningvms|cut -d" " -f 1); do
      echo "$machine"
    done
    

    【讨论】:

    • “经典方法”是不好的做法,array=( $(...) ) 方法也是如此;例如,两者都将用当前目录中的文件列表替换输入中的单个标记*。请参阅Don't Read Lines With ForBashPitfalls #50BashFAQ #1
    • 此特定示例需要分词,并且 VM 名称与 glob 模式匹配似乎不太可能。
    • 我遇到的最糟糕的数据丢失事件是因为有人忽略了“不可能”事件(创建文件的程序“不可能”创建任何不匹配的内容 [0-9a-f ]{24}; 3rd 方库中的一个错误损坏了一个指针,将随机垃圾转储到用于名称的缓冲区中,其中包含一个被空格包围的 * ......以及一个迭代文件名以老化的 shell 脚本退出备份系统,然后删除所有内容)。
    • 更重要的是,我们在这里教授的做法将在其他地方和其他地方使用——除非答案中有很大的免责声明,否则它们应该可以安全地广泛使用。跨度>
    • 好的。我现在没有办法重现该命令的输出,所以不能做一个“完美”的 anwser。随意添加您自己的。
    【解决方案2】:
    VBoxManage list runningvms | sed 's/"//g;s/ .*//'
    

    循环:

    for machine in `VBoxManage list runningvms | sed 's/"//g;s/ .*//'` ; do
        echo $machine
    done
    

    如果您的机器名称中有空格,这将中断。

    【讨论】:

      【解决方案3】:
      VBoxManage list runningvms | cut -d '"' -f 2 | while read machine; do
         echo "$machine"
      done
      

      【讨论】:

      • 这比我的要好 - 如果图像名称中有空格,我的会失败......没想到。
      • while read ... do 语法正是我想要的!
      • 你可以通过写while IFS='"' read _ machine _; do取出cut
      【解决方案4】:

      对于单线粉丝:

      VBoxManage list runningvms | cut -d" " -f 1 | grep -oP "(?<=\").*(?=\")"
      

      【讨论】:

        【解决方案5】:

        要在阅读时验证每一行,安全的做法是编写一个正则表达式并使用BASH_REMATCH 从中提取匹配组。

        使用以下代码:

        re='^"(.*)" [{]([0-9a-f-]+)[}]$'
        while read -r line; do
          if [[ $line =~ $re ]]; then
            name=${BASH_REMATCH[1]}; uuid=${BASH_REMATCH[2]}
            echo "Found VM with name $name and uuid $uuid" >&2
          else
            echo "ERROR: Could not parse line: $line" >&2
          fi
        done < <(VBoxManage list runningvms)
        

        ...以及以下 VBoxManage 的模拟实现(以允许没有 VirtualBox 的人重现测试):

        VBoxManage() { printf '%s\n' '"UbuntuServer" {7ef01f8d-a7d5-4405-af42-94d85f999dff}'; }
        

        ...输出如下:

        Found VM with name UbuntuServer and uuid 7ef01f8d-a7d5-4405-af42-94d85f999dff
        

        注意这种方法的优点:

        • 它不会做出毫无根据的假设,例如将名称中带有空格或引号文字的虚拟机排除在支持之外。
        • 它会检测任何与预期模式不匹配的行,而不是在存在此类值时以意想不到的方式表现。
        • 对于确实与模式匹配但具有意外值的数据,它仍能正常运行。 (例如,名为 * 的虚拟机不会将该名称默认替换为当前目录中的文件名)。
        • 它不涉及 shell 外部的工具,例如 sedcut 等,而是完全依赖于 shell 内置功能 -- 请参阅 BashFAQ #1 记录使用 while read 和 @ 987654322@ 记录[[ $string =~ $re ]]

        【讨论】:

          【解决方案6】:

          VBoxManage list runningvms|awk -F \{ '{gsub("\"",""); print $1}'

          对于VBoxManage 输出中的每一行,这会去除" 字符,然后打印第一个字段(当使用{ 作为字段分隔符时,它会启动vbox vm 的UID)。

          除了转义特殊字符外,无需检查空格或以不同方式处理输出的任何部分,或执行任何 shell 操作。

          我发现awkcutgrepsed 等更通用,而且它也更具表现力。它自 1987 年以来一直存在,自 1992 年以来一直是 POSIX 标准:与 bash 相比,您的系统上更有可能拥有 awk

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-01-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-07-16
            • 2013-08-24
            • 2015-11-01
            相关资源
            最近更新 更多