从Bash中的变量列表获取字符串

时间:2019-06-07 14:40:36

标签: bash variables

我有一个与此相似的文件(file.env

kw_var1='string1'
kw_var2='string 2'
kw_var3='this is string 3'
kw_var4='does not matter'
kw_var5='maybe'

w_var1=qwert_1
w_var2=qwert_2
w_var3=qwert_3
w_var4=qwert_4

我需要创建一个字符串list_of_values,其中包含以kw_开头的所有变量的值,即

$ echo -e $list_of_values

应输出:

'string1' 'string 2' 'this is string 3' 'does not matter' 'maybe' 

我试图遍历它们,但无法使其正常工作。我的代码:

list_of_values=$(for param in $(cat $file.env | grep "kw\_"); do echo $(echo -e '$'$param | cut -s -d '=' -f1); done)

但这就是我得到的:

 $kw_var1 $kw_var2 $kw_var3 $kw_var4 $kw_var5

请注意:

  • 变量值将包含空格;
  • list_of_values将用作另一个函数的参数

有什么问题的主意吗?

更新:

当我做最后的回声时,我使用了:

$ echo -e $list_of_values | tr '\n' ' '

将所有内容集中在一起。

5 个答案:

答案 0 :(得分:3)

尝试输入代码

我尝试了您的命令,并将其作为输出:

$kw_var1
$kw_var2

$kw_var3



$kw_var4


$kw_var5

输出错误,因为使用cut时选择的是第一个字段,而不是第二个。


修复剪切命令

for param in $(cat test.txt | grep "kw\_"); do echo $(echo '$'$param | cut -s -d '=' -f2); done

返回:

'string1'
'string
'this


'does

'maybe'

修复IFS

您使用了for in循环,但是它没有遍历换行符,而是遍历空格。您需要先更改IFS(内部字段分隔符)变量:

IFS=$'\n'; for param in $(cat <file> | grep "kw\_"); do echo $(echo $param | cut -s -d '=' -f2); done

输出:

'string1'
'string 2'
'this is string 3'
'does not matter'
'maybe'

使用printf

要在一行上获得输出,可以使用printf代替echo

for param in $(cat <file> | grep "kw\_"); do printf "$(echo $param | cut -s -d '=' -f2) "; done; printf "\n"

输出:

'string1' 'string 2' 'this is string 3' 'does not matter' 'maybe' 

同时使用

您可以简化命令并使用直接在行上进行迭代的while read语句:

cat <file> | grep "kw\_" | cut -d"=" -f2 | while read line; do printf "${line} "; done; printf "\n"

使用awk

最后但并非最不重要的是,您可以使用awk来从根本上简化您的代码:

awk -F"=" '/kw_/{printf "%s ", $2}END{print ""}' <file>

输出:

'string1' 'string 2' 'this is string 3' 'does not matter' 'maybe'

如果行尾的多余空间令人讨厌,则可以这样做:

awk -F"=" '/kw_/{printf "%s%s", delim, $2; delim=" "}END{print ""}' <file>

Awk解释了:

# Using = as delimiter
awk -F"=" '
    # If line contains kw_
    /kw_/{
        # Prints second field
        printf "%s%s", delim, $2;
        delim=" "
    }
    END{
        # Prints newline
        print ""
    }' <file>

最终代码

list_of_values=$(awk -F"=" '/kw_/{printf "%s%s", delim, $2; delim=" "}END{print ""}' $file.env)

答案 1 :(得分:2)

$ cat tst.awk
/^kw_/ {
    sub(/[^=]+=/,"")
    str = str sep $0
    sep = " "
}
END {
    print str
}

例如请注意,它可以正确处理所需输出字符串中的this=that

$ cat file
kw_var1='string1'
kw_var2='string 2'
kw_var3='this is string 3'
kw_var4='does not matter'
kw_var5='maybe'
kw_var6='this=that'

w_var1=qwert_1
w_var2=qwert_2
w_var3=qwert_3
w_var4=qwert_4

$ awk -f tst.awk file
'string1' 'string 2' 'this is string 3' 'does not matter' 'maybe' 'this=that'

已更新:鉴于您现在在评论中告诉我们的内容,这是假设您有时需要通过其标签访问各个值的方式,否则,可以使用常规数组代替关联性:

$ cat tst.sh
#!/bin/env bash

declare -A kw
declare -A w
while IFS= read -r line; do
    tag="${line%%=*}"
    val="${line#*=}"
    case "$tag" in
        kw* ) kw["$tag"]="$val" ;;
        w*  ) w["$tag"]="$val" ;;
        ?*  ) printf 'Error: unexpected contents: "%s"\n' "$line"; exit  1;;
    esac
done < file.env

printf '\nAll kw indices => values:\n'
for idx in "${!kw[@]}"; do
    printf '\t%s => %s\n' "$idx" "${kw[$idx]}"
done

printf '\nAll kw values passed to a function (printf) at once:\n'
printf '\t%s\n' "${kw[@]}"

printf '\nAll w indices => values:\n'
for idx in "${!w[@]}"; do
    printf '\t%s => %s\n' "$idx" "${w[$idx]}"
done

printf '\nAll w values passed to a function (printf) at once:\n'
printf '\t%s\n' "${w[@]}"

$ ./tst.sh

All kw indices => values:
        kw_var4 => does not matter
        kw_var5 => maybe
        kw_var6 => this=that
        kw_var1 => string1
        kw_var2 => string 2
        kw_var3 => this is string 3

All kw values passed to a function (printf) at once:
        does not matter
        maybe
        this=that
        string1
        string 2
        this is string 3

All w indices => values:
        w_var3 => qwert_3
        w_var2 => qwert_2
        w_var1 => qwert_1
        w_var4 => qwert_4

All w values passed to a function (printf) at once:
        qwert_3
        qwert_2
        qwert_1
        qwert_4

上面的代码在此file.env上运行,而值周围没有多余的单引号,否则您只需在脚本中将其删除:

$ cat file.env
kw_var1=string1
kw_var2=string 2
kw_var3=this is string 3
kw_var4=does not matter
kw_var5=maybe
kw_var6=this=that

w_var1=qwert_1
w_var2=qwert_2
w_var3=qwert_3
w_var4=qwert_4

wrt our discussion in the comments并使用printf '<%s>\n'代替fitsort我不知道也没有:

$ list[0]='foo bar'; list[1]='etc'

$ printf '<%s>\n' "${list[@]}"
<foo bar>
<etc>

$ printf '<%s>\n' $(printf '%s\n' "${list[@]}")
<foo>
<bar>
<etc>

$ printf '<%s>\n' "$(printf '%s\n' "${list[@]}")"
<foo bar
etc>

看看第一个版本如何正确地简单地将list[]的内容传递给fitsort-replacement命令,而其他版本则将printf输出的字符串传递给它吗?

答案 2 :(得分:1)

我使用了动态引用。

$: out="$( . file.env; for r in ${!kw_*}; do printf "'%s' " "${!r}"; done; echo )"
$: echo "$out"

'string1' 'string 2' 'this is string 3' 'does not matter' 'maybe'

答案 3 :(得分:0)

将一堆变量组成两个数组,然后您可以像这样轻松地对其进行迭代

#!/bin/bash

kw=(
'string1'
'string 2'
'this is string 3'
'does not matter'
'maybe'
)

w=(
'qwert_1'
'qwert_2'
'qwert_3'
'qwert_4'
)

for i in {1..5}
do
    echo -n "\"${kw[$i]}\" "
done

echo

for i in {1..4}
do
    echo -n "\"${w[$i]}\" "
done

echo

答案 4 :(得分:0)

您的尝试使用了一些不推荐的做法,并且包含一些语法错误:

  • 您使用的是$file.env,但应该只是file.env
  • 请勿使用for从文件中读取行(请参见this article
  • echo $(cmd)cmd相同,但具有通配符和分词功能,这通常不是您想要的
  • echo -e '$'$param将打印文字$符号
  • cut -f1选择第一个字段,但您要第二个字段

这是您本着尝试的精神解决方案,但仅使用Bash:

list=$(
    while IFS='=' read -r key val; do
        [[ $key == kw_* ]] && printf '%s ' "$val"
    done < file.env
)
list=${list% }   # Remove trailing blank

但是,如果处理包含空格的字符串,通常建议改用数组。由于file.env是有效的Bash,我们可以获取感兴趣的行,然后使用所有kw_变量的值构建一个数组:

source <(grep '^kw_' file.env)
declare -n var
for var in "${!kw_@}"; do list+=("$var"); done

该数组现在每个元素包含一个字符串,而没有文字单引号:

$ printf '%s\n' "${list[@]}"
string1
string 2
this is string 3
does not matter
maybe

declare -nvar设置为一个nameref:将其视为实际上是其名称所包含的变量。这需要Bash 4.3或更高版本。