【问题标题】:Reading delimited output into multiple Bash variables将分隔输出读入多个 Bash 变量
【发布时间】:2019-01-31 10:56:57
【问题描述】:

这应该很简单,我以前在某个脚本中做过,但找不到我的示例(或等效示例),而今天这个问题让我发疯了。 (即使我没有包含脚本的其余部分,这是在脚本中使用的,而不是交互式的。)

cat testfile | grep -e eth0

返回:

eth0         123.45.67.8/23                  u/u  Internet - Cable WAN

最终结果是我需要为每个元素设置变量。即好像我是手动完成的:

INTF = "etho"
IPADDR = "123.45.67.8/23"
STS = "u/u"
DESC = "Internet - Cable WAN"

我想我可以这样做:

cat testfile | grep -e eth0 | awk '{print $2}' | xargs read IPADDR

cat testfile | grep -e eth0 | cut -d " " -n2 | read IPADDR

但我所尝试的一切都没有带来快乐......我的障碍是什么(头障)?

编辑添加——该脚本比仅获取一个 IP 更复杂,正如我的示例引导人们得出的结论。这是一个基于 cron 的脚本,每分钟运行一次,它通过 8 个接口循环运行,并在某些警报条件下发送消息。当我使用硬编码变量运行脚本时,脚本的其余部分可以正常工作,我只是询问了困扰我的部分。

【问题讨论】:

  • 为什么不直接使用IPADDR=$(cat outputfile | grep -e eth0 | awk '{print $2}')
  • 顺便说一句,在您对问题的描述中,您在第一个示例中使用outputfile 作为文件名,在第二个示例中使用testfile。小心点。
  • @NéstorLucasMartínez 这是一种可能性,我没想过这样分配,但是我的示例过于简单,因为这是一个基于 cron 的脚本,每分钟运行一次,并循环通过 8 个接口。 (这不仅仅是一个获取单个 IP 并运行的脚本。)当然,我实际上并不需要设置 INTF,因为它在那时已经知道,但在每个循环中按照建议执行 3 次似乎效率低下。
  • 那你可以试试read INTF IPADDR STS DESC <<< `cat testfile | grep -e eth0`

标签: bash


【解决方案1】:

在你的命令中

cat testfile | grep -e eth0 | cut -d " " -n2 | read IPADDR
cat testfile | grep -e eth0 | awk '{print $2}' | xargs read IPADDR

读取命令在子 shell 中运行,对主 shell 不可见。

您需要process substitution 将值一次性放入父 shell:

read -r intf ipaddr sts desc < <(grep eth0 testfile)

前三个变量将获取 grep 输出中前三个字段的值(这是基于 IFS 的字符串拆分),第四个变量 desc 将获取输出中剩余的标记。


顺便说一句,cat outfile | grep -e eth0UUOC 的一个例子。只需 grep eth0 outfile 即可完成您的工作。


相关:

【讨论】:

    【解决方案2】:

    有很多方法可以做到这一点,您甚至可以扫描您的测试文件并将结果放入一对一匹配的数组中:

    #!/bin/bash
    file="testfile.txt"
    
    declare -a intf
    declare -a ipaddr
    declare -a sts
    declare -a desc
    
    # - file reader -
    while IFS= read -r line; do
        if [[ ${line,,} == *"intf"* ]];then
            intf+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)")
        elif [[ ${line,,} == *"ipaddr"* ]];then
            ipaddr+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)")
        elif [[ ${line,,} == *"sts"* ]];then
            sts+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)")
        elif [[ ${line,,} == *"desc"* ]];then
            desc+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)")
        fi
    done < "$file"
    
    if [[ ${#intf[@]} -eq  ${#ipaddr[@]} &&  ${#intf[@]} -eq ${#ipaddr[@]} &&  ${#intf[@]} -eq  ${#sts[@]} ]] ;then
        echo "file read successful! continuing .."
    else
        echo "icky, this didn't work"
    fi
    
    for ((i=0; i< ${#intf[@]}; i++)) ;do
           echo -e "INTF=${intf[$i]} \nIPADDR=${ipaddr[$i]} \n"
    
    done
    

    输出(类似):

    $ ./script.sh
    file read successful! continuing ..
    INTF=etho 
    IPADDR=123.45.67.8/23 
    
    INTF=etho 
    IPADDR=13.45.67.8/23 
    
    INTF=etho 
    IPADDR=23.45.67.8/23 
    

    【讨论】:

    • 在问题中添加了一段,更好地解释了需求,而不是试图仅仅获取单个互联网连接的外部 IP。
    • 谢谢我误读了你的问题,上面是一种制作数组的方法,并在扫描文件后检查你是否有一对一的关系。
    • desc+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)") 等可以用read 更高效地编写,而不需要调用子shell?
    • 是的,可能没有理由使用回声
    【解决方案3】:

    由于你想设置 4 个变量,而不是做 cut 4 次,你可以像这样使用 read

    #!/bin/bash
    #
    read INTF IPADDR STS DESC <<< $(cat testfile | grep -e eth0)
    
    echo $INTF
    echo $IPADDR
    echo $STS
    echo $DESC
    

    这将使用默认的$IFS“剪切”任何空白。

    如果您想从以下位置删除值:“aaa,bbb,ccc,ddd”, 您可以更改read之前的IFS值。

    例如:

    IFS="," read INTF IPADDR STS DESC <<< $(cat testfile | grep -e eth0)
    

    【讨论】:

    • 这正是我想要做的,一次读完所有 4 个。这行得通。 Bash 不是我的正常环境,子 shell 总是给我带来麻烦。
    【解决方案4】:

    如果你想使用read一次性获取所有赋值的变量,你可以这样做:

    read INTF IPADDR STS DESC <<< `cat testfile | grep -e eth0`
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      • 2023-03-22
      • 2017-02-25
      • 2014-07-22
      • 2023-04-08
      • 2013-06-11
      相关资源
      最近更新 更多