【问题标题】:Associative array in bash to store all lines start with Xbash中的关联数组存储所有以X开头的行
【发布时间】:2014-11-15 14:29:19
【问题描述】:

我有一个文件,其中包含我通过 $1 输入的行:

X B C D E
X G H I J
X L M N 
Y G  
Z B
Y L

每行以 X 开头,键是第二个元素,值是其余元素。

我正在逐行读取文件,为每个文件创建关联数组。

while read LINE
do
 INPUT=$(echo $LINE |awk '{print $1}')
 if [[ "$INPUT" = X ]]
 then
     key_name=$(echo $LINE | awk '{print $2}')
     declare -A dependencies 
     value_names=($(echo $LINE|awk '{$1=$2=""; print $0}'))
     dependencies[key_name]=value_names
     echo -e "\nvalues of $key_name are ${key_name[*]}\n"
     sleep 1
 fi
done < $1

所以我失去了每行读数的价值。

但我需要将所有带有 X 的行存储在关联数组中, 因为我需要稍后为后面的行搜索键,假设:一行以 Y 开头,它有G,所以在这里我需要从关联的数组中找到值 用 G 键。

谁能建议一些想法,如何通过读取文件行来将所有以 X 开头的行存储在单个关联数组中?还是有更好的方法?

从这里给出的示例输入,输出将分为 3 行:

H I J
C D E
M N

这里 X,Y,X 正在识别行,如何处理下一个字符。如果 X 将其余部分存储在 KEY-PAIR 中,或者如果 YZ 从关联数组中提取值。

【问题讨论】:

  • 在您的示例中只有一行以X 开头。
  • 是的@mgamba,只有行以 X 开头。我需要逐行读取文件,因为我还必须读取以不同字符开头的其余行。
  • 什么是pkg_depends
  • declare -A dependencies 行在循环内做什么?
  • 任何时候你在 shell 中编写一个循环只是为了操作文本你有错误的方法。操作文本的标准 UNIX 工具是 awk,所以只需使用它。根据您的示例输入发布预期输出,以便我们为您提供帮助。

标签: arrays linux bash unix awk


【解决方案1】:

声明应该在循环之外。变量插值前面需要一个美元符号。剩下的只是重构。

declare -A dependencies
awk '$1=="X"{$1=""; print }' "$1" |
{ while read -r key value; 
  do
    dependencies["$key"]="$value"
    echo -e "\nvalues of $key_name are ${key_name[*]}\n"
    #sleep 1
  done
  :
  # do stuff with "${dependencies[@]}"
}

【讨论】:

  • +1,但也就是说,echo -e?我们真的想要处理扩展变量中的转义序列吗? printf 将允许更好的控制。
  • @tripleee,我无法从外部检索键值。我该怎么办?
  • 糟糕,我以为我在更新答案时留下了评论。另见mywiki.wooledge.org/BashFAQ/024
  • @PDK,另一种方法(如果您的 shell 是 bash)是将 awk | while ... done 替换为 while ... done &lt; &lt;(awk ...),这也会导致您的变量在循环之外保留。
【解决方案2】:

将 GNU awk 用于 gensub():

$ gawk '{ if (/^X/) a[$2] = gensub(/(\S+\s+){2}/,"",""); else print a[$2] }' file
H I J
C D E
M N

上面隐式循环遍历输入文件中的每一行,当它找到以 X (/^X/) 开头的行时,它会删除前 2 个非空格-然后-空格对 (gensub(/(\S+\s+){2}/,"","")) 并存储导致关联数组a由原始第二个字段(a[$2] = ...)索引,因此例如对于输入行X B C D E,它保存a["B"] = "C D E"。如果该行不是以 X (else) 开头,那么它会打印由当前行中的第二个字段索引的数组,因此对于输入行 Z B,它将执行 print a["B"] 并因此输出 C D E

使用旧版本的 gawk(运行 gawk --version 并检查 4.0 之前的版本)您可能需要:

$ gawk --re-interval '{ if (/^X/) a[$2] = gensub(/([^[:space:]]+[[:space:]]+){2}/,"",""); else print a[$2] }' file

但如果是这样的话,你会遗漏很多非常有用的功能,那就换个新的傻瓜吧!

【讨论】:

  • 我以为它是 100+ 班轮,但让我尝尝 awk 的力量。 :)。让我为我的目的试一试。其实我是在拿到值后对它们做一些操作。
  • Mortan,我的 awk oneliner 输出错误。我得到了输出:X G H I JX B C D EX L M N 3 行,X 和密钥包括在内。
  • 检查您的输入文件是否有 WIndows 在它创建的文件上添加的尾随 control-Ms,如果存在,请使用 dos2unix 删除它们。否则,请编辑您的问题以显示您正在执行的实际脚本以及您运行它的实际输入,因为鉴于您发布的示例输入文件,我发布的那个不会这样做。如果您发布另一个问题,其中包含示例输入、预期输出以及对这些其他操作的清晰描述,我希望您也会获得一个简洁、强大、简单的 awk 解决方案。
  • @EdMortan,我使用的是 linux,文件中没有 control-M。但是对于我的示例输入,我得到了错误的输出,就像我之前的评论一样。我正在做一些简单的操作,比如安装包。在我的示例输入中,假设:如果我想安装包G,我必须先安装HIJ。然后安装G。这些H, I, J 是从以X 开头的行收集的。
  • 哦,等一下 - 如果您使用的是非常旧的(4.0 之前)版本的 gawk,则默认情况下不会启用 RE 间隔,因此请尝试在前面添加 --re-interval 标志。此外,如果仅此一项不起作用,请尝试将 \S 替换为 [^[:space:]] 并将 \s 替换为 [[:space:]] ,如果其中一个或两个都得到更新版本的 gawk!我刚刚编辑了我的答案,以添加一个也适用于 4.0 之前的 gawks 的版本。
猜你喜欢
  • 2015-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-11
  • 1970-01-01
  • 1970-01-01
  • 2019-07-01
  • 1970-01-01
相关资源
最近更新 更多