在bash中分配和/或操作来自外部程序的传入变量(字符串)

时间:2018-06-20 12:09:11

标签: bash

我有一个外部程序,可以通过stdin($ 1)将大量信息交给我的脚本。

我得到如下一行:

session number="2018/06/20-234",data name="XTRDF_SLSLWX3_FSLO",data group="Testing",status="Error",data type="0"

现在我要使用此行拆分为单个变量。

到目前为止,我考虑了两种方法:

INPUT='session number="2018/06/20-234",data name="XTRDF_SLSLWX3_FSLO",data group="Testing",status="Error",data type="0"'
echo "$INPUT" | tr ',' '\n' | tr ' ' '_' > vars.tmp
set vars.tmp

这将一直执行到我有一个空格的data_name变量,我的trim命令会自动将其更改为_,并且在以后的检查中我分配的变量不再正确。

所以我考虑将输入加载到数组中,并对数组进行一些模式替换以删除所有内容,直到并包括=为止,然后再进行一些变量赋值

INPUT='session number="2018/06/20-234",data name="XTRDF_SLSLWX3_FSLO",data group="Testing",status="Error",data type="0"'
IFS=',' read -r -a array <<< "$INPUT"
array=("${array[@]/#*=/}")
session_number="${array[0]}"
data_name="${array[1]}"
....

但是,如果数据名称或数据组中某处有=,那么现在我有一个奇怪的行为会切断输入,并且我不知道这是否是这样做的方法。我敢肯定,与空格相比,数据名称或数据组字段中不应包含=,但您永远不会知道...

我该怎么办?

1 个答案:

答案 0 :(得分:1)

简单的情况:字符串中没有逗号

如果您不必担心引用数据中的逗号或文字引号,则以下内容可以处理您所问的情况(数据中的杂散=):

#!/usr/bin/env bash
case $BASH_VERSION in ''|[123].*) echo "ERROR: Requires bash 4.0 or newer" >&2; exit 1;; esac

input='session number="2018/06/20-234",data name="XTRDF_SLSLWX3_FSLO",data group="Testing",status="Error",data type="0"'
declare -A data=( )
IFS=, read -r -a pieces <<<"$input"
for piece in "${pieces[@]}"; do
  key=${piece%%=*}   # delete everything past the *first* "=", ignoring later ones
  value=${piece#*=}  # delete everything before the *first* "=", ignoring later ones
  value=${value#'"'} # remove leading quote
  value=${value%'"'} # remove trailing quote
  data[$key]=$value
done
declare -p data

...结果(添加空格以提高可读性,否则输出文字):

declare -A data=(
  ["data type"]="0"
  [status]="Error"
  ["data group"]="Testing"
  ["data name"]="XTRDF_SLSLWX3_FSLO"
  ["session number"]="2018/06/20-234"
)

处理引号内的逗号

现在,假设您要做需要担心引号内的逗号!考虑以下输入:

input='session number="123",error="Unknown, please try again"'

现在,如果我们尝试在不考虑逗号位置的情况下分割逗号,我们将使用error="Unknownplease try again作为杂散值。

要解决此问题,我们可以将GNU awk与FPAT功能一起使用。

#!/usr/bin/env bash
case $BASH_VERSION in ''|[123].*) echo "ERROR: Requires bash 4.0 or newer" >&2; exit 1;; esac

input='session number="123",error="Unknown, please try again"'

# Why do so many awk people try to write one-liners? Isn't this more readable?
awk_script='
BEGIN {
    FPAT = "[^=,]+=(([^,]+)|(\"[^\"]+\"))"
}

{
    printf("%s\0", NF)
    for (i = 1; i <= NF; i++) {
        printf("%s\0", $i)
    }
}
'

while :; do
  IFS= read -r -d '' num_fields || break
  declare -A data=( )
  for ((i=0; i<num_fields; i++)); do
    IFS= read -r -d '' piece || break
    key=${piece%%=*}
    value=${piece#*=}
    value=${value#'"'}
    value=${value%'"'}
    data[$key]=$value
  done
  declare -p data # maybe invoke a callback here, before going on to the next line
done < <(gawk "$awk_script" <<<"$input")

...此后输出正确:

declare -A data=(["session number"]="123" [error]="Unknown, please try again" )