如何替换文件中某些行的子字符串中的所有空格?

如何替换文件中某些行的子字符串中的所有空格?

我有 3000 个名为“journal/*.md”的 Markdown 文件,其中一些包含如下文件链接:

blah blah text [Label here](file:///path/to file/may contain/spaces)

我想更改此类文件中的所有此类行,以在 file:// URL 中包含 %20:

blah blah text [Label here](file:///path/to%20file/may%20contain/spaces)

这让我几乎到了那里,通过炉管:

for file in */*md; do if grep -l '(file:.*[ ].*)' "$file"; then echo FOUND in $file; sed 's?\((file://.*[ ].*)\)?\nREPLACED_SED1 \1?g' "$file" > "$file".sed1; sed '/REPLACED_SED1/s/ /%20/g' "$file".sed1 > "$file".sed2; fi; done

这给我留下了一个弗兰肯文件,其行分割如下:

blah blah text [Label here]
REPLACED_SED1(file:///path/to%20file/may%20contain/spaces)

我被困住了,因为如何将两条线粘在一起? Perl 来拯救?
谢谢!

答案1

我建议通过使用保留空间将替换仅应用于一行的一部分来进行一次运行尝试:

sed 'h;s/.*(file://;s/ /%20/g;x;s/(file:.*//;G;s/\n/(file:/'
  1. 将该行复制到h旧空间。稍后我们将在该行的第一部分中使用它
  2. 删除分割模式之前的所有内容:s/.*(file://
  3. 进行替换:s/ /%20/g
  4. Ex更改缓冲区,以便我们可以处理第一部分
  5. 现在删除第二部分,从模式开始:s/(file:.*//
  6. G:通过将第二部分的保留空间附加到第一部分的模式空间来连接两个部分
  7. 现在用分隔模式(我们从两个部分中删除)替换换行符(通过附加嵌入):s/\n/(file:/

答案2

使用 GNU awk 作为第三个参数match()

$ awk 'match($0,/(.*\(file:)(.*)(\).*)/,a) { gsub(/ /,"%20",a[2]); $0=a[1] a[2] a[3] } 1' file
blah blah text [Label here](file:///path/to%20file/may%20contain/spaces)

match()您可以使用任何带有+的 awk 来执行相同的操作substr(),只需再输入几个字符:

$ awk 'match($0,/\(file:.*\)/) { tgt=substr($0,RSTART,RLENGTH); gsub(/ /,"%20",tgt); $0=substr($0,1,RSTART-1) tgt substr($0,RSTART+RLENGTH) } 1' file
blah blah text [Label here](file:///path/to%20file/may%20contain/spaces)

答案3

尝试:

for file in */*md; do 
    while grep -l '(file:.*[ ].*)' "$file"; do
        sed -i 's/\(.*file:[^)]*\) \(.*\)/\1%20\2/' "$file"
    done
done

解释:

  • 当文件名中有空格(您自己的 grep)时,执行
  • 将行分为三部分:空格之前、空格和空格之后
  • 替换为第一部分“%20”和第三部分。

由于当时每行仅替换一个空格,因此需要循环。举个例子:

input:         blah blah text [Label here](file:///path/to file/may contain/spaces)
1st iteration: blah blah text [Label here](file:///path/to file/may%20contain/spaces)
2nd iteration: blah blah text [Label here](file:///path/to%20file/may%20contain/spaces)

答案4

只需调整您的代码并删除 sed 的第二次调用,因为这是不必要的。

for f in ./*/*md; do
  # select a nonlinked, regular, nonempty file

  [ ! -L "$f" ] &&
  [   -f "$f" ] && 
  [   -s "$f" ] &&
  < "$f" grep -q '(file://.*[ ].*)' ||
  continue

  echo "FOUND in $f"

  sed -e ':a
    s#\((file://.*\)[ ]\(.*)\)#\1%20\2#
    t a
  ' < "$f" > "$f.modif" &&
  mv -f "$f.modif" "$f"

done

相关内容