【问题标题】:How to store a stream in a variable?如何将流存储在变量中?
【发布时间】:2014-04-23 18:25:24
【问题描述】:

这是我的脚本:

for country in AT DE GB IT NO ZA
do
    info1=$(cat /path/to/big.log* | grep $country | wc -l)
    info2=$(cat /path/to/other-big.log* | grep $country | wc -l)
    echo $country $info1 $info2
done

这行得通,但真的很慢......

我已经尝试过这个脚本,而是将文件读取置于循环之外:

data1=$(cat /path/to/big.log*)
data2=$(cat /path/to/other-big.log*)
for country in AT DE GB IT NO ZA
do
    info1=$(echo $data1 | grep $country | wc -l)
    info2=$(echo $data2 | grep $country | wc -l)
    echo $country $info1 $info2
done

但现在脚本不起作用。我错过了什么?

【问题讨论】:

  • grep 是在我们想要提取特定表达式时提高速度的好主意...

标签: bash variables for-loop stream


【解决方案1】:

将整个文件内容存储到变量中并不是一个好主意。你可以试试这个bash 脚本,

for country in AT DE GB IT NO ZA
do
    info1=$(grep $country /path/to/big.log* | wc -l)
    info2=$(grep $country /path/to/other-big.log* | wc -l)
    echo $country $info1 $info2
done

我刚刚移除了不需要的管道。

【讨论】:

  • 这个解决方案比我的要快一点,但仍然很慢:\ 该脚本在具有(大量)内存的服务器上运行。为什么存储不应该是一个好主意?
  • 您对bash 有什么期望,以及您的机器配置...?
  • @Alex,如果您将文件的全部内容存储到variable,那么它将占用大量 RAM。它会导致其他进程变慢。我用250M 文件进行了测试。
  • 为什么要放两个grep 来搜索同一个国家?喜欢使用word=$(grep $country /path/to/*.log* | wc -lbatter。
【解决方案2】:

您在脚本中不必要地使用了catwc

grep -c试试这个脚本:

for country in AT DE GB IT NO ZA
do
    info1=$(grep -c "$country" /path/to/big.log*)
    info2=$(grep -c "$country" /path/to/other-big.log*)
    echo "$country $info1 $info2"
done

【讨论】:

  • 我已经尝试过这个解决方案。但是,grep 输出文件名后跟行数:/path/to/my-file:99。如何只保留数字?
  • 如果有多个文件匹配/path/to/big.log*,你想要哪个?在这种情况下你想要所有计数的总和吗?
  • grep -c "$country" /path/to/big.log* | awk -F: '{print $1}'
【解决方案3】:
cat /tmp/file1 /tmp/file2 | for country in AT DE GB IT NO ZA; do
    printf "%s " "$country"
    grep -c "$country"
done

由于您有多个文件,我认为这是获得所需输出的最快方法。如果我错了,请有人纠正我。

【讨论】:

    【解决方案4】:

    您的基本方法是错误的,因为您要完整地遍历每个国家/地区的每组文件,而不是一次计算每个国家/地区。这是一个bash 4 脚本,但可以用其他更快的语言实现。

    # Untested; consider this a description of the algorithm rather than
    # ready-to-run code.
    declare -A counts1=()
    declare -A counts2=()
    # Store per-country counts in an associative array for the first set of files
    # E.g. counts[AT]=28, counts[DE]=93, etc.
    for f in /path/to/big.log*; do
      while read line; do
        [[ $line =~ (AT|DE|GB|IT|NO|ZA) ]] && (( $counts1[${BASH_REMATCH[0]}]++ ))
      done  < $f
    done
    # Do the same for the second set of files
    for f in /path/to/other-big.log*; do
      while read line; do
        [[ $line =~ (AT|DE|GB|IT|NO|ZA) ]] && (( $counts2[${BASH_REMATCH[0]}]++ ))
      done  < $f
    done
    
    # Output the results
    for cc in AT DE GB IT NO ZA; do
        printf "%s %d %d\n"  $cc ${counts1[$cc]} ${counts2[$cc]}
    done
    

    【讨论】:

    • 在我看来,您的基本算法揭示了 BASH 的一些高级概念。您能否提供未经测试且更易于运行的代码?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 1970-01-01
    • 2014-03-14
    • 2020-02-03
    • 2011-07-15
    • 1970-01-01
    相关资源
    最近更新 更多