简体   繁体   中英

Iterate two arrays simultaneously - bash n sed

I need help with executing two arrays simulataneously in bash and using sed to replace a word with the words in the second array in each separate file. There are two lists:

List1 - contains the names of the directories, which have similar parameter file "surfnet.par" in each directory (the file in different directories have the same name "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 - contains numbers corresponding to each directory which has to be replaced with a specific word (single occurrence) in the file "surfnet.par" existing in different directories

List2.txt

11351
11357
11371
11384
11350
11373
11383
11365
11377
11382

To make it still clear

I want to replace a word "Resnum" in "surfnet.par" in the directory "3IT6_1" of List1 with "11351" of List2, likewise, replace the same word in "surfnet.par" of 3IT6_2 with 11357, 3IT6_3 with 11357, 3IT6_6 with 11371 and so on.

I have tried pushing the list to array and then using a for loop to replace the word, but failed in doing so, as it took the first value of List2 and replace in all "surfnet.par" files in different directories. The script I have been using is as below:

#!/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

I will appreciate it very much, if any of you can help me resolve this code and point out the modification that needs to be done. In case, my code doesn't make any sense please provide me the solution using bash, awk, sed or perl

I think you may have been a whole lot closer to a solution than you thought. This is one situation in bash where making use of a c-style loop and iterating on an index can come in very handy. The following slight changes to your code should work, give it a try (note: I added a check on cd and used the starting directory as current to enable use of absolute paths):

#!/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

Example Use

Tested with your ListX.txt files with cd and sed calls commented out.

$ 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

Note: in bash for indexed arrays, the use of the $ on the index variable is not required. (eg ${dir[$i]} is fine as ${dir[i]} . It is treated the same as if it were enclosed in ((..)) as in the loop declaration.

Note2: you should probably add a validation that both values are available at the top of the loop before calling cd to change to the desired directory:

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

Your question bears a Perl tag so I assume that Perl solutions are acceptable

Your question isn't very clear, but I think this program should help you

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;
}

If you don't like to type too much, you can do this

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

appropriately replace target with your search word, f.txt f2.txt list1 and list2 with the file names you use. It should be clear which is which.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM