【问题标题】:Bash - Multiple replace with sed statementBash - 用 sed 语句多次替换
【发布时间】:2023-03-23 06:15:01
【问题描述】:

我对脚本表演很生气。

基本上我必须替换 35000 多个文件中的 600 个字符串。

我有这样的东西:

patterns=(
   oldText1 newText1
   oldText2 newText2
   oldText3 newText3
)

pattern_count=${#patterns[*]}

files=(`find \. -name '*.js'`);
files_count=${#files[*]}

for ((i=0; i < $pattern_count ; i=i+2)); do
    search=${patterns[i]};
    replace=${patterns[i+1]};
    echo -en "\e[0K\r Status "$proggress"%. Iteration: "$i" of " $pattern_count;
    for ((j=0; j < $files_count; j++)); do
        command sed -i s#$search#$replace#g ${files[j]};
        proggress=$(($i*100/$files_count));
        echo -en "\e[0K\r Inside the second loop: " $proggress"%. File: "$j" of "$files_count;
    done
    proggress=$(($i*100/$pattern_count));
    echo -en "\e[0K\r Status "$proggress"%. Iteration: "$i" of " $pattern_count;
done

但这需要花费大量时间。还有另一种解决方案吗?可能只使用一次 sed 而不是双循环?

非常感谢。

【问题讨论】:

  • 您可以使用sed -e 链接替换,并通过将与号传递给这些命令来并行运行多个进程,例如sed -e 's/word1/replace1/' -e 's/word2/replace2' &amp;

标签: bash unix sed replace


【解决方案1】:

创建一个合适的sed 脚本:

s/pattern1/replacement1/g
s/pattern2/replacement2/g
...

使用sed -f script.sed file(或任何需要的方式)运行此脚本。

您可以使用您的数组创建 sed 脚本:

printf 's/%s/%s/g\n' "${patterns[@]}" >script.sed

将其应用于文件:

find . -type f -name '*.js' -exec sed -i -f script.sed {} ';'

我不太清楚 GNU sed(我假设您正在使用)在您使用 -i 时如何处理多个文件,但您可能也想尝试一下

find . -type f -name '*.js' -exec sed -i -f script.sed {} +

这可能会更有效率(执行尽可能少的sed 命令)。与往常一样,对测试后可以丢弃的数据进行测试。

有关将-execfind 结合使用的更多信息,请参阅https://unix.stackexchange.com/questions/389705

【讨论】:

  • 目前在服务器上执行这种方式,似乎速度快了很多;每个文件只有一次迭代,没有进度条,但我猜好的部分是性能提高。非常感谢@Kusalananda
  • @CristianBatista 您可以在-exec 之前插入-print 以查看它至少找到了哪些文件......这将告诉您它已经走了多远。这将在终端中产生 35000 行输出...
  • 是的,这很有用,但我猜会明显变慢。我会把它花费的全部时间放在最后
  • 最后是 35 分钟,而不是我估计的 16 小时。你摇滚
  • GNU sed 与 -i 选项和多个文件在每个文件的基础上运行,因为该选项意味着 -s/--separate 选项。
【解决方案2】:
  1. 您不需要对一个文件多次运行 sed。您可以使用 ';' 分隔 sed 命令
  2. 您可以并行执行多个 sed

例如:

patterns=(
   oldText1 newText1
   oldText2 newText2
   oldText3 newText3
)
// construct sed argument such as 's/old/new/g;s/old2/new2/g;...'
sedarg=$(
for ((i = 0; i < ${#patterns[@]}; i += 2)); do
        echo -n "s/${patterns[i]}/${patterns[i+1]}/g;"
done
)

// find all files named '*.js' and pass them to args with zero as separator
// xargs will parse them:
//  -0 use zero as separator
//  --verbose will print the line before execution (ie. sed -i .... file)
//  -n1 pass one argument/one line to one sed
//  -P8 run 8 seds simulteneusly (experiment with that value, depends on how fast your cpu and harddrive is)
find . -type f -name '*.js' -print0 | xargs -0 --verbose -n1 -P8 sed -i "$sedarg"

如果你非常需要进度条,我猜你可以数行xargs --verbose返回或更好地使用parallel --bar,参见this post

【讨论】:

  • 我将在@Kusalananda 的命令之后使用此命令,这是一个很棒的解决方案,-P8 很有趣,非常感谢您的输入
猜你喜欢
  • 1970-01-01
  • 2011-12-02
  • 2021-11-20
  • 1970-01-01
  • 2013-08-31
  • 1970-01-01
  • 1970-01-01
  • 2020-09-22
  • 1970-01-01
相关资源
最近更新 更多