shell 用位于另一个文件中的变量替换文件中的字符串

sy5wg1nm  于 2023-04-21  发布在  Shell
关注(0)|答案(5)|浏览(140)

我想用存在于另一个文件的每一行中的几个变量替换文本文件中的字符串,然后以新名称保存目标文件。下面是一个示例和我的代码:

$ cat ref            # contains variables as the name in each line
item1
item2
item3

$ cat main           # main inventory. the string xxx will be replaced with item# and modified file to be saved under the same variable name
sale inventory
this record is for xxx
end of inventory

所需的输出文件[3个文件。下面是item1]:

$ cat item1
sale inventory
this record is for item1
end of inventory

我的代码awk

awk -v fname=ref($0) '/xxx/ {$5 = fname}1' main >> "$fname"

Error: syntax error: got (, expecting Newline

先谢谢你的帮忙

thtygnil

thtygnil1#

可以使用类似的awk方法来收集数组中的记录。下面的ref项目被收集在item数组中,来自main的记录被收集在main数组中。END规则用于在main的第二个记录上进行替换,将输出重定向到以项目命名的文件,例如:

awk '
  FNR == NR {                         # if record from 1st file
    item[++n] = $1                    # store in indexed item array
    next                              # skip to next record
  }
  { main[++m] = $0 }                  # for 2nd file, store record in main
  END {                               # after all records processed
    for (i=1; i<=n; i++)              # loop over all items
      for(j=1; j<=m; j++) {           # loop over each record in main
        line = main[j]                # save record in line
        if (j == 2)                   # if 2nd record in main
          sub(/xxx/, item[i], line)   # substitue item for xxx, save in line
        print line >> item[i]         # append line to item file 
      }
  }
' ref main

注意:if (j == 2)行只是限制了sub(...)被调用到第二行的次数,如果你想检查并替换每一个"xxx"出现的itemX,你可以去掉if(...),然后选择gsub(...)sub(...),这取决于每行"xxx"是否可以出现多个)

示例使用/输出

在包含refmain文件的目录下的命令行中粘贴上述内容,将产生:

$ l item*
-rw-r--r-- 1 david david 57 Apr 15 10:06 item1
-rw-r--r-- 1 david david 57 Apr 15 10:06 item2
-rw-r--r-- 1 david david 57 Apr 15 10:06 item3

内容:

$ cat item1
sale inventory
this record is for item1
end of inventory

$ cat item2
sale inventory
this record is for item2
end of inventory

$ cat item3
sale inventory
this record is for item3
end of inventory

如果你有问题就告诉我。

f2uvfpb9

f2uvfpb92#

一种awk方法:

awk '
FNR==NR { names[$0]; next }               # 1st file; save entries as indices of the names[] array
        { for (fname in names) {          # 2nd file; loop through names[] indices
              line=$0                     # make copy of current input line
              gsub(/xxx/,fname,line)      # replace "xxx" with contents of "fname" variable
              print line > fname          # print modified line to file
          }
        }
' ref main

备注:

  • 这将为每个新的输出文件维护一个打开的文件描述符
  • 对于“大量”文件,这可能会导致某些版本的awk中止,并出现错误,指出它耗尽了文件描述符
  • 虽然这个问题可以通过额外的编码来解决,但我们需要更好地了解两个文件的预期最大大小(ref中的行数,main中的MB字节)

这产生:

$ head item?
==> item1 <==
sale inventory
this record is for item1
end of inventory

==> item2 <==
sale inventory
this record is for item2
end of inventory

==> item3 <==
sale inventory
this record is for item3
end of inventory
t1qtbnec

t1qtbnec3#

为了实现它,你可以像这样在循环中使用awk

#!/bin/bash

vars=$(cat ref.txt)
template=main.txt

for var in $vars; do
    awk -v var="$var" '{gsub(/xxx/, var)}1' "$template" > "output_$var"
done

使用sed的替代版本:

#!/bin/bash
vars=$(cat ref.txt)
template=main.txt

for var in $vars; do
    sed "s/xxx/$var/g" "$template" > "output_$var"
done
u3r8eeie

u3r8eeie4#

这里的解决方案只有awk后,创建一个输出目录out/,我使用的是附带的readfile扩展。

mkdir out
awk '@load "readfile"; BEGIN {main = readfile("main"); backup = main}; {sub(/xxx/, $0, main); print main > "out/" $0; main = backup}' ref
ru9i0ody

ru9i0ody5#

我会用Ruby来做这样一个项目:

ruby -e '
template=File.open(ARGV[1]).read
File.open(ARGV[0]).
    each_line.with_index(1){|tgt,i| 
        fn=File.open("#{ARGV[1]}_#{i}","w")
        fn.write(template.sub(/\bxxx\b/, tgt.chomp))
        fn.close
}
' ref main

或者Perl:

perl -0777 -nE '
    if(!$#ARGV){@arr=split(/\R/, $_); next}
    $s=$_;
    for $e (@arr){ 
        open(FH, ">", "${ARGV}_${\(++$cnt)}") || die;
        s/\bxxx\b/$e/; 
        print FH;
        close(FH);
        $_=$s; 
    }
' ref main

或者你可以使用Bash/sed:

# fast to write, great for small files, not so great for huge 'main'
while IFS= read -r tgt; do
    (( ++cnt ))
    sed -E "s/xxx/${tgt}/" main > "main_${cnt}"
done  < ref

任何这些结果:

$ head main_*
==> main_1 <==
sale inventory
this record is for item1
end of inventory

==> main_2 <==
sale inventory
this record is for item2
end of inventory

==> main_3 <==
sale inventory
this record is for item3
end of inventory

相关问题