【问题标题】:How to iterate lines from a file and read fields into variables如何迭代文件中的行并将字段读入变量
【发布时间】:2019-12-27 16:54:42
【问题描述】:

我有一个文件hotfix_final,看起来像这样:

https://download.abc.com  06/24/2019
https://download.abc.com  06/26/2019
https://download.abc.com  07/05/2019

我需要编写一个 shell 脚本,逐行读取文件,然后将链接旁边的日期放入一个变量中,以便我可以将其与当前日期进行比较。如果日期相同且usage_type = 4,我需要脚本来获取链接。

到目前为止我尝试了什么:

usage_type=$( cat /opt/abc/ps/usage.txt )
current_date=$( date +%x )
lines=$( wc -l /home/abc/hotfix_final | awk '{print $1}' )

count=0

while $count <= $lines; do

    hf_link=$( awk if( NR=$count ) '{print $1}' hotfix_final )
    relase_date=$( awk if( NR=$count ) '{print $2}' hotfix_final )
    count=$(( count+1 ))

done < hotfix_final

在上面的例子中,我使用了:

  • $lines 显示要读取的最大行数。

  • $hf_link获取链接

  • $release_date 获取 $hf_link 旁边的日期

现在,我不确定如何编写检查$usage_type == 4$current_date = $relase_date 是否为真的部分,如果是,请获取链接。这需要为文件的每一行单独完成。

【问题讨论】:

  • 这个脚本有很多很多很多的错误。 (1):正确引用您的 awk:awk '{if( NR==$count ) print $1}'(2) 如果语句应该进行比较而不是赋值:if(NR == $count)(3) shell 变量应该正确地传递给 awk:awk -v count=$count '{if(NR == count) print $1}' (4) 不要像你那样用 while 循环解析 bash 中的文件:,只需使用适当的 while 与 read 结合,您可以跳过 awk 来提取变量。查看Léa Gris的回答
  • 请参阅stackoverflow.com/a/38627863/874188,了解为什么读取行号通常是一种反模式。

标签: bash shell loops awk


【解决方案1】:

这可能对你有用(GNU Parallel):

[ $(<usageFile) -eq 4 ] && 
parallel -a fixFile -C' +' [ {2} = $(date +%m/%d/%Y) ] \&\& wget {1}

使用test 查询使用文件,如果设置为4,则使用并行完成任务。 Parallel 使用fix 文件和带有一个或多个空格的正则表达式的-C 选项将文件中的列命名为{1} 作为url,{2} 作为日期。再次使用测试来检查日期列与今天的日期,匹配将wget url。

【讨论】:

    【解决方案2】:

    可以通过对脚本进行一些修复来完成:

    • 您需要注意引用变量以避免值被空格或$IFS 变量中列出的任何字符分割。

    • date +%x 将在$LC_TIME 环境变量中具有不同区域设置的系统上返回具有不同格式的日期。
      设置 LC_TIME=en_US 时,%x 格式将是 MM/DD/YYY,但有一个很小的机会(虽然公认的可能性很小)en_US 语言环境可能对系统不可用。
      然后最好使用明确的独立于语言环境的格式+%d/%m/%Y,以保证日期格式的安全。

    这是一个固定版本:

    #!/usr/bin/env bash
    # Early exit this script if the usage.txt file does not contain the value 4
    grep -Fqx 4 /opt/abc/ps/usage.txt || exit
    
    # Store current date in the MM/DD/YYYY format
    current_date="$(date +%d/%m/%Y)"
    
    # Iterate each line from hotfix_final
    # and read the variables hf_link and release_date
    while read -r hf_link release_date; do
      if [ "$current_date" = "$release_date" ]; then
        wget "$hf_link"
      fi
    done </home/abc/hotfix_final # Set the file as input for the whole while loop
    

    【讨论】:

    • 但是shell如何知道hotfix_final文件的每一行$hf_link代表链接,而$release_date代表链接旁边的日期?
    • read 命令一次读取一行。然后,当出现多个要读取的变量时,它使用空格或$IFS 环境变量中定义的字符来分隔行内的字段,并按照与读取变量参数相同的顺序为变量赋值。
    • 如果usage_type 不是 4,你想提前退出,而不是在循环中一次又一次地比较它。
    • 您只是移动了它,它仍在每次迭代中进行比较。
    • 抱歉,我的回答中出现了fgrep -f 错字,您也想在这里解决这个问题。
    【解决方案3】:

    这是对已接受答案的重构,以避免丑陋的while read -r 循环。

    #!/bin/sh
    
    grep -Fqx 4 /opt/abc/ps/usage.txt || exit
    
    awk -v current_date="$(date +%d/%m/%Y)" '
        $2 == current_date { print $1 }' /home/abc/hotfix_final |
    xargs -r -n 1 wget
    

    xargs-r 选项是 GNU 扩展;如果您没有它,它并不重要,但有助于避免在 awk 脚本不产生任何输出时出现错误消息。

    在您的下一个项目中,您希望确保在计算机可读文件中使用不那么疯狂的日期格式。

    【讨论】:

    • 我猜你可以在一个独立的 awk 脚本中使用它自己的#!/usr/bin/env awkshebang
    • 我也是从 Awk 脚本中的 usage.txt 处理开始的,但对于可能不太熟悉 Awk 的人来说,它的意义不大,所以我想保持它非常简单和结构化。
    猜你喜欢
    • 2019-06-25
    • 2012-01-12
    • 2020-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    相关资源
    最近更新 更多