BASH - 解析带有特殊字符的字符串

BASH - Parse strings with special characters

目标:我正在尝试创建 docker ps 的交互式版本。基本上,让每一行都是 "menu" 这样用户就可以:启动、停止、ssh 等

示例:

   CONTAINER ID        IMAGE             COMMAND                  CREATED             STATUS              PORTS                                   NAMES
1. bf4a9c7de6bf        app_1             "docker-php-entryp..."   7 days ago          Up About an hour    443/tcp, 0.0.0.0:80->80/tcp, 9000/tcp   app_1
2. 26195f0764ce        app_2             "sh /var/www/html/..."   10 days ago         Up About an hour    443/tcp, 127.0.0.1:8000->80/tcp         app_2

选择(1/2 等)后,将出现一个选项菜单,用于对所选容器执行各种操作。

问题:我似乎无法弄清楚如何解析 docker ps 命令的每一行,以便我将容器 ID 和其他值作为数组元素。

目前的代码:

list=`docker ps`
IFS=$'\n' array=($list)

for index in ${!array[@]}
do
  declare -a 'a=('"${array[index]}"')'
  printf "%s\n" "${a[@]}"  
done

结果:

CONTAINER
ID
IMAGE
COMMAND
CREATED
STATUS
PORTS
NAMES
/usr/bin/dockersh: array assign: line 9: syntax error near unexpected token `>'
/usr/bin/dockersh: array assign: line 9: `bf4a9c7de6bf        app_1             "docker-php-entryp..."   7 days ago          Up About an hour    443/tcp, 0.0.0.0:80->80/tcp, 9000/tcp   app_1'

您的引用似乎有一些问题,也许可以试试:

list=$(docker ps)
IFS=$'\n' array=($list)

for index in "${!array[@]}"
do
  declare -a a=("${array[index]}")
  printf "%s\n" "${a[@]}"  
done

如果没有正确引用,您的字符串可能会被重新拆分;考虑检查你的 shell 脚本@ shell-check.net,因为它通常会给你一些关于错误语法的好提示。

如果您想要一个关联数组,其特征是一个矩阵,您的所有 docker ps 字段都可以在 row/column 中访问,您可以使用 awk 插入分隔符 |场之间。然后将结果导出到单个关联数组中,并根据您期望的列数(例如 7)构建矩阵:

#!/bin/bash
IFS=$'|'

data=$(docker ps -a | awk '
function rtrim(s) { sub(/[ \t\r\n]+$/, "", s); return s }
{
    if (NR == 1) {
        head[1] = index([=10=],"CONTAINER ID")
        head[2] = image=index([=10=],"IMAGE")
        head[3] = command=index([=10=],"COMMAND")
        head[4] = created=index([=10=],"CREATED")
        head[5] = status=index([=10=],"STATUS")
        head[6] = ports=index([=10=],"PORTS")
        head[7] = names=index([=10=],"NAMES")
    }
    else{
        for (i = 1;i < 8;i++) {
            if (i!=7){
                printf "%s",rtrim(substr([=10=], head[i], head[i+1] - 1 - head[i])) "|"
            }
            else{
                printf "%s",rtrim(substr([=10=], head[i], 100)) "|"
            }
        }
        print ""
    }
}')

arr=($data)
max_column=7
row=0
column=0

declare -A matrix

for index in "${!arr[@]}"
do
    matrix[$row,$column]=$(echo "${arr[index]}" | tr -d '\n')
    column=$((column+1))
    if [ $((column%max_column)) == 0 ]; then
        row=$((row+1))
        column=0
    fi
done

echo "first  container ID   is : ${matrix[0,0]}"
echo "second container ID   is : ${matrix[1,0]}"
echo "third  container NAME is : ${matrix[2,6]}"

awk部分,目的是在每个字段之间插入一个|字符,以便将要注入的数据与|分隔符[=20]关联数组=]

由于字段内容与字段标题对齐,我们将每个字段名称的索引存储在head数组中,并根据下一个字段位置提取每个字段裁剪

然后 matrix 是根据最大列数 (7) 构建的。然后可以使用 ${matrix[row,column]}

轻松访问每个 row/column

通常情况...除非您确切知道格式以及如何控制它,否则不要使用 for 循环读取数据:

while IFS="\n" read -r line
do
  array+=("$line")
done< <(docker ps)

就我个人而言,我会尝试删除行首的数字(1.、2. 等),因为这样您就可以将其放入 select 中,它会为您提供数字,然后可以用于引用相关项目。