如何多次用文本文件中的随机行替换字符串

时间:2014-12-18 15:33:29

标签: linux bash random sed shuffle

我有一个脚本,用于通过文件旋转并用文件中的随机行替换占位符{{su}},占位符在文件中多次出现,我需要它是一个每次随机。目前,它用相同的行替换每个占位符。

#!/bin/bash

subject=$(shuf -n1 *.subjects)
    cat tmp.$file | sed -e "s/{{su}}/$subject/" > output.file

3 个答案:

答案 0 :(得分:2)

接受的答案有微妙的缺陷:

  • 如果{{su}}在同一行上多次出现,则会为该行的每个模式{{su}}执行相同的替换
  • 因为read未与IFS=-r开关一起使用,您会得到其他令人讨厌的惊喜:空间不一定会被保留,您将获得反斜杠解释(但这很容易修复),
  • 如果替换字符串包含斜杠或其他有趣的字符,sed将会混淆。

一种有效的方法,但它涉及在内存中读取整个文件(它仅适用于少量{{su}}的小文件):

#!/bin/bash

file=$(< filename.txt )

while [[ $file = *'{{su}}'* ]]; do
    repl=$(shuf -n1 file.subjects)
    file=${file/'{{su}}'/"$repl"}
done
printf '%s\n' "$file"

对于类似于接受的答案的方法,即逐行阅读:

#!/bin/bash

while IFS= read -r line; do
    while [[ $line = *'{{su}}'* ]]; do
        repl=$(shuf -n1 file.subjects)
        line=${line/'{{su}}'/"$repl"}
    done
    printf '%s\n' "$line"
done < filename.txt

现在关于选择随机行的方法:虽然shuf很好,但是它是一个外部进程,因为它将被多次调用(在子shell中),你可以考虑在Bash中实现类似的东西。如果您的行数量有限,您可以考虑将所有行拖入数组并从该数组中随机选择一个条目:

#!/bin/bash

mapfile -t replacements < file.subjects
nb_repl=${#replacements[@]}

while IFS= read -r line; do
    while [[ $line = *'{{su}}'* ]]; do
        repl=${replacements[RANDOM%nb_repl]}
        line=${line/'{{su}}'/"$repl"}
    done
    printf '%s\n' "$line"
done < filename.txt

这仅适用于file.subjects中的“小”行数(小,理解小于32767),并且如果您不太担心模数获得的分布。但是,有一些非常简单的解决方法可以解决这个问题。

注意。您正在使用shuf -n1 *.subjects。使用多个文件调用shuf是错误的(至少使用我的shuf版本)。因此,如果glob *.subjects扩展为多个文件,则会出错。

注意。如果您不想遇到无限循环,请确保替换不包含{{su}}模式!

答案 1 :(得分:1)

你需要一个循环。首先使用wc -l计算tmp.$file中的行数。 然后循环计数次数,每次执行你拥有的两行shell脚本。 因此,在每个循环中,您将获得一个新主题并执行新的sed。诀窍是使用sed命令的地址,地址格式一次执行对一行的替换,传入地址的循环计数器。

类似于(伪代码):

$count = $(wc -l tmp.$file)    
$i=1    
cp tmp.$file > output.file    
while $i < $count    
 subject = $(shuf -n1 *.subjects)    
 cat output.file | sed -e "$i,$is/{{su}}/$subject/" > output.file    
 $i=$i+1    
end while

答案 2 :(得分:1)

在这种情况下,您需要在文件中逐行迭代,并在每次迭代时生成随机字符串。它将检查{{su}}模式的所有行,如果发现它将用来自另一个文件的随机字符串替换它:

while read line
do
subject=$(shuf -n1 *.subjects)
sed -e "s/{{su}}/$subject/g" <<< "$line")
done <1.txt