BASH:读入数组并选择字段

时间:2015-10-06 23:13:12

标签: arrays bash shell sorting field

我有两个问题......

首先,我有一组已定义的信息,我将(echo -e)回显到文件" / tmp / replacementments " ......(截断版):

"RU_SIT1_CA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nDEV1_DA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nDEV1_DP-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTDEV1_DA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTDEV1_DP-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTSIT1_DA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTSIT1_DP-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nSIT1_CA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nDEVX1_CA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTDEV1_CA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nDEVX1_DA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nDEVX1_DP-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nSIT1_DA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nSIT1_DP-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nDEV1_CA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nRU_SIT2_CA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nDEV2_DA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nDEV2_DP-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTDEV2_DA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTDEV2_DP-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTSIT2_DA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nFTSIT2_DP-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\nSIT2_CA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000" > "/tmp/replacements"

我尝试将该文件读入数组:

read -a OLDIP "/tmp/replacements"
echo "${OLDIP[@]}"

执行脚本时,shell只挂起而不返回。没有。我不明白为什么。 " Bash Cookbook"," ShellCheck"和" Man page"所有人都说read -a应该有效,附带echo "${OLDIP[@]"

我还尝试使用WHILE循环:

while IFS= read -a line
do
    line=$REPLY
done <<< "/tmp/replacements"

echo "${line[@]}"

这次,执行时,shell不会挂起,它只是输出任何内容。

如果我这样做的话,&#34; Dirty Way&#34; (LOL),它有效:OLDIP=$(cat "/tmp/replacements") ...因为我还在学习,我尝试使用&#34;最佳实践&#34;只要有可能,从我所看到的......虽然这可能没有错,但肯定有更好的方法。 (另外,我想将每个WORD分配给数组中的元素,当我想要存储LINES时)

这是第一个问题......现在是第二个......

我遇到的第二个问题是如何从阵列中选择一个字段?

我试图在&#34; / etc / hosts &#34;中替换IP地址。 ...我想运行脚本并说:

# search "/etc/hosts" for an IP we have in the array
# if a match is found, perform a SED to replace that
# IP with the IP found in position #3 of that line
# (LB_FQDN,OLDIP,NEWIP)

for i in "${line[@]}"
    do
        grep -q "$i" "/etc/hosts"
            if [[ "$?" -eq "0" ]] #when grep is quiet (-q) exit status 0 means a match was found.
                then
                    sed -i "s/$i/THE-MATCHING-IP-WAS-FOUND,-REPLACE-IT-WITH-THE-SECOND-IP-IN-THAT-SAME-LINE/g" "/etc/hosts"
            fi
    done

假设我可以让我的阵列实际填充&#34; 正确的方式&#34;,我真的很感激如何搜索&#34; / etc / hosts /的一些指导&#34;对于在#2字段中找到的IP,如果找到,请将其替换为同一行上的IP,字段#3。

我非常感谢你的帮助。

干杯! -Alex。

3 个答案:

答案 0 :(得分:1)

read -a OLDIP <<< $(cat /tmp/replacements)
echo "${OLDIP[@]}"

for item in ${OLDIP[@]}
do
   item=( $(echo $item | tr ',' ' ') )
   sed -i "s/${item[1]}/${item[2]}/" /etc/hosts  
done

我认为是这样的。假设线数据中从不存在空白区域。如果有,你必须使用IFS并在循环中重置它,然后在下一个循环之前设置为原始。

解释,重新评论请求:

  1. &#34;&lt;&lt;&lt;&#34;不同于&#34;&lt;&#34;或&#34;&lt;&lt;&#; A.发送变量中包含的数据,在这种情况下:$(cat / tmp / replacementments),用于任何使用它的数据。
  2. 我写了这么多bash,我老老实实地停止使用某些难以阅读或给出不可靠结果或不一致的结构。例如,在你的,我先试过&lt; / tmp /替换但它没有用。所以我只是切换到我一直使用的东西以及将永远有效的东西,我注意到我的一个大bash程序中有两个地方使用&lt;对于文件中的一些数据,但我没有写出这些行,也从来没有真正弄清楚它们是如何工作的,或者为什么,哈哈。

    &GT;意思是:写入文件,如:echo yes&gt; file.txt,并创建该文件,覆盖内容。

    &GT;&GT;表示:附加到文件,该文件应该已经存在。

    &LT;意味着从文件中读取,但在特殊意义上,它不会按照您的预期或您期望的方式执行。我从不使用它,几乎从不使用它。

    &LT;&LT;不存在。

    我的io规则很简单: &GT;写入某个东西,然后创建它/设置为null。 当然,回声fred&gt; / dev / null之类的东西,只是让它消失,stderr / stdout重定向等。 &GT;&GT;附加到文件 &LT;&LT;&LT;从任何东西中读取,如果是文件,我就抓住它。

    http://tldp.org/LDP/abs/html/x17837.html

    A here string can be considered as a stripped-down form of a here document.
    It consists of nothing more than COMMAND <<< $WORD,
    where $WORD is expanded and fed to the stdin of COMMAND.
    

    &LT;&LT;&LT;就像滚边一样,但我相信它更有效率。

     cat /etc/hosts | command
     # works much like:
     command <<< $(cat /etc/hosts)
    

    除非函数本身处理文件,例如grep something filename,sed -i&#39; s / stuff / stuff /&#39;文件名等

    http://www.tldp.org/LDP/abs/html/io-redirection.html

    如果您不熟悉该网站,请将其加入书签,这是其中最好的BASH资源之一。

    0< FILENAME
    < FILENAME
      # Accept input from a file.
      # Companion command to ">", and often used in combination with it.
      #
      # grep search-word <filename
    

    即使这个例子也有点奇怪,因为你真的这样做了:

    grep search-word filename
    

    我无法诚实地解释bash的用途&lt;文件名,因为我从不使用那个结构,它是不可预测的,我更喜欢bash是可预测的,所以我把它愚蠢用于我的使用,顺便说一下工作得很好。在您的情况下,它创建了一个包含1个项目的数组。

    2:我看到我们正在设置&#34; item&#34;的值作为它在数组中发现的结果...我们如何确定我们的数组有LINES而不是WORDS?

    item=( $(echo $item | tr ',' ' ') )
    

    item =(单词单词单词)就是这样的结果,每个结果数组都是一行分成&#39;单词&#39;通过&#39;,&#39;到&#39; &#39;翻译,从文件,因此,你有一个bash数组使用默认的bash内部字段分隔IFS的&#39; &#39;

    http://tldp.org/LDP/abs/html/internalvariables.html

    读取页面重新IFS。 IFS不是简单的解释,所以除非你有一个特定的问题,我将把它留在tldp页面解释。

    我对此问题并不完全清楚。我的文件基于给定的用户数据样本,它显然永远不会有空格,也就是说,单词,即它是一条线。如果数据可以有空格,则需要更多处理。

    RU_SIT1_CA-dev.ext.foo.bar.com,123.456.789.000,123.456.789.000\n
    
    例如,

    是给定的一行。域名没有空间,IP 1和2不会有空格。我假设它们是动态生成的,因此该行不会有超过1的单词,即它是一个没有提供空格的字符串。

    如果需要明确的空间处理,我会使用IFS =&#39;,&#39;来创建数组。在循环中,然后从那里继续。但是,&#39;,&#39; &#39; &#39;是生成bash空间分隔数组的最快/最简单的方法,bash默认值。

    1. 此外,我知道&#34; tr&#34;用于修剪从管道接收的输入,但这是说它修剪逗号并用什么都替换它?
    2. tr用于替换单个字符,而不是修剪。可以这样想:tr(anslate),即它将x转换为y,在这种情况下,&#39;,&#39;到&#39; &#39 ;.逗号不会被替换为空,它被替换为&#39; &#39;,一个空间。

      man tr:
      NAME
             tr - translate or delete characters
      DESCRIPTION
             Translate, squeeze, and/or delete characters from standard input, writing to standard output.
      

      tr是在shell中将一个字符更改为另一个字符的最快/最简单的方法。

      这就是为什么我不能使用&lt;

      read -a OLDIP <<< $(cat /tmp/replacements);for item in ${OLDIP[@]}; do item=( $(echo $item | tr ',' ' ') );echo ${item[1]}/${item[2]};done
      
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      123.456.789.000/123.456.789.000
      

      但是,使用&lt;

      read -a OLDIP < /tmp/replacements;for item in ${OLDIP[@]}; do item=( $(echo $item | tr ',' ' ') );echo ${item[1]}/${item[2]};done
      

      你得到:

      123.456.789.000/123.456.789.000
      

      即,只有一行,无论是第一行还是最后一行,可能是最后一行。我相信会发生的事情是在每行读取时都会创建一个新数组OLDIP,因此最终只能得到最后一行数组,而使用&lt;&lt;&lt;你一下子吐出整个东西,然后读取所有的线条并做你想要的,创建包含所有线条的阵列。我测试了这个:

      awk '{print $0}' < /tmp/replacements
      

      可以按照您的预期运作,因此这似乎是特定于阅读的内容-a。老实说,我从来没有亲自用这种方式生成数据来生成数组,我本来只是用另一种方法直接生成数组,但是因为那是你提出问题的方法,那就是我认为的方法有趣的是找出它为什么不起作用。

      鉴于:

      <<< $(cat /tmp/replacements)
      

      总是毫无例外地完全按照您的预期行事,将整个文件吐回读取-a。

      我认为这可以回答你的其他问题。

      我写得如此荒谬,以至于我不再使用任何难以记忆,难以理解,难以阅读或难以调试的功能,或者不能使用你期望他们的方式。

答案 1 :(得分:0)

您可以使用此awk脚本替换整个问题:

#!/usr/bin/awk -f
NR == FNR {
  z[FNR][1]
  split($0, z[FNR], ",")
  next
}
$1 == z[FNR][2] {
  $1 = z[FNR][3]
}
1

然后像这样跑:

awk /tmp/replacements /etc/hosts

答案 2 :(得分:-1)

如果您想继续使用read代替mapfilereadarray,可以执行此操作:

read -d '' -r -a A < replacements

echo "${A[@]}"