【问题标题】:Iterate two arrays simultaneously - bash n sed同时迭代两个数组 - bash n sed
【发布时间】:2015-08-22 06:03:23
【问题描述】:

在 bash 中同时执行两个数组并使用 sed 将一个单词替换为每个单独文件中第二个数组中的单词时,我需要帮助。有两个列表:

List1 - 包含目录的名称,每个目录都有类似的参数文件“surfnet.par”(不同目录下的文件具有相同的名称“surfnet.par”)

List1.txt

3IT6_1
3IT6_3
3IT6_6
3IT6_9
3IT6_11
3IT6_12
3IT6_19
3IT6_23
3IT6_54
3IT6_62

List2 - 包含对应于每个目录的数字,必须用不同目录中存在的文件“surfnet.par”中的特定单词(单次出现)替换

List2.txt

11351
11357
11371
11384
11350
11373
11383
11365
11377
11382

为了更清楚

我想将List1的“3IT6_1”目录下“surfnet.par”中的一个单词“Resnum”替换为List2的“11351”,同样将3IT6_2的“surfnet.par”中的同一个单词替换为11357, 3IT6_3 与 11357,3IT6_6 与 11371 等等。

我尝试将列表推送到数组,然后使用 for 循环替换单词,但这样做失败了,因为它采用了 List2 的第一个值并替换了不同目录中的所有“surfnet.par”文件。我一直使用的脚本如下:

#!/bin/bash

declare -a dir
declare -a res

dir=(`cat "List1.txt" `)
res=(`cat 'List2.txt'`)

for i in "${dir[@]}"
    do 
        echo $i
        cd $i
        sed -e "s/Resnum/${res$[0]}/g" surfnet.par > surfnet2.par
        cd ..
    done

如果你们中的任何人能帮助我解决此代码并指出需要进行的修改,我将不胜感激。如果我的代码没有任何意义,请使用 bash、awk、sed 或 perl 为我提供解决方案

【问题讨论】:

  • 您的叙述说您想替换单词Resnum,而您的代码替换LATHA。它是哪个,这个词在哪里指定?
  • 您使用的是哪个版本的bashreadarray -t dir < Listdirs204.txt 比使用 cat 更安全,因为它不会对文件的内容进行 shell 处理(分词、路径名生成等)。
  • 抱歉用词不正确。我已将其更改为 Resnum。

标签: bash perl awk sed


【解决方案1】:

我认为您可能比您想象的更接近解决方案。这是 bash 中的一种情况,其中使用 c-style 循环并迭代索引非常方便。对您的代码进行以下细微更改应该可以工作,试一试(注意:我在cd 上添加了一个检查并将起始目录用作current 以启用绝对路径的使用):

#!/bin/bash

declare -a dir
declare -a res

dir=( $(<List1.txt) )
res=( $(<List2.txt) )

current="$PWD"

for ((i = 0; i < ${#dir[@]}; i++))
    do 
        cd "$current/${dir[i]}" || {
            echo "failed to change to ${dir[i]}"
            continue
        }
        printf "%3d  %-8s Resnum -> %s\n" $i ${dir[i]} ${res[i]}
        sed -e "s/Resnum/${res[i]}/g" surfnet.par > surfnet2.par
    done

使用示例

使用您的 ListX.txt 文件进行测试,其中 cdsed 调用已注释掉。

$ bash resnum.sh
  0  3IT6_1   Resnum -> 11351
  1  3IT6_3   Resnum -> 11357
  2  3IT6_6   Resnum -> 11371
  3  3IT6_9   Resnum -> 11384
  4  3IT6_11  Resnum -> 11350
  5  3IT6_12  Resnum -> 11373
  6  3IT6_19  Resnum -> 11383
  7  3IT6_23  Resnum -> 11365
  8  3IT6_54  Resnum -> 11377
  9  3IT6_62  Resnum -> 11382

注意: 在 bash 中对于 indexed 数组,不需要在索引变量上使用 $。 (例如,${dir[$i]}${dir[i]} 一样好。它的处理方式与在循环声明中包含在 ((..)) 中一样。

注意2:在调用cd 更改到所需目录之前,您可能应该在循环顶部添加两个值都可用的验证:

        ## validate both values available
        [ -z ${dir[i]} -o -z ${res[i]} ] && {
            echo "'${dir[i]}' or '${res[i]}' missing."
            continue
        }

【讨论】:

    【解决方案2】:

    如果你不喜欢打字太多,你可以这样做

    while read d s; do sed 's/target/'"$s"'/g' "$d"/f.txt > "$d"/f2.txt; done < <(paste list1 list2)
    

    用您的搜索词适当地替换target,用您使用的文件名替换f.txt f2.txt list1list2。应该清楚哪个是哪个。

    【讨论】:

    • 谢谢,此脚本已用于根据需要编辑文件
    【解决方案3】:

    您的问题带有 Perl 标签,因此我认为 Perl 解决方案是可以接受的

    你的问题不是很清楚,但我认为这个程序应该可以帮助你

    use strict;
    use warnings;
    use v5.10.1;
    use autodie;
    
    my @dirs = slurp('Listdirs204.txt');
    my @res  = slurp('LastHetatmRes.txt');
    
    die "File sizes don't match" unless @dirs == @res;
    
    for my $i ( 0 .. $#dirs ) {
    
        my ($dir, $res) = ($dirs[$i], $res[$i]);
        my $file        = "$dir/surfnet.par";
    
        my @lines = slurp($file);
        s/Resnum/$res/g for @lines;
    
        open my $fh, '>', $file;
        print $fh "$_\n" for @lines;
        close $fh;
    }
    
    
    sub slurp {
        open my $fh, '<', shift;
        my @lines = <$fh>;
        chomp @lines;
        @lines;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-04-11
      • 2015-05-26
      • 1970-01-01
      • 2016-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多