【问题标题】:bash/sed/awk: replace arbitrary substring in arbitrary filesbash/sed/awk:替换任意文件中的任意子字符串
【发布时间】:2021-07-28 15:22:50
【问题描述】:

更新:

我已经尝试了 markp-fuso 的答案,它就像一个魅力

我开始感到沮丧,因为我不是 bash/sed 之类的日常用户。

起点:

我有许多包含许多源文件的子文件夹 (.c,.cpp,.cxx)。 在项目文件 (.vcxproj) 中引用这些源文件进行编译。

我想做的事:

我想查找所有包含字符串#import 的源文件。然后我想找到引用这些源文件的所有项目文件。 然后我想在这些项目文件中就地编辑这些引用的所有出现 例如<Include="folder/file.cpp"/> -> <Include="folder/file.cpp" Attribute="Value"/>

我尝试过的:

egrep -lir --include=*.{c,cpp,cxx} "(#import)" ./e3 | xargs -L 1 basename | egrep -ir --include=*.vcxproj -f - ./e3 | sed 's/:/ /g'

生成这样的列表:

./src/base/base.vcxproj     <ClCompile Include="Folder1\Folder1File1.cpp" />
./src/mod/mod.vcxproj     <ClCompile Include="Folder2\Folder2File1.cpp" />
./src/ext/ext.vcxproj     <ClCompile Include="Folder3\Folder3File1.cpp" />

于是我尝试了

egrep -lir --include=*.{c,cpp,cxx} "(#import)" ./e3 | xargs -L 1 basename | egrep -ir --include=*.vcxproj -f - ./e3 | sed 's/:/ /g' | awk '{ sed -iE 's/($2,$3)/\1 Attribute="Value"/g' }'

哪些错误发生了

bash: syntax error near unexpected token `('

我尝试了一个带有 shell 脚本的解决方案,但它也不起作用,我不知道是否以及如何解决上述错误消息。我愿意接受任何解决方案,只要它在 bash 中运行,甚至可能比我想出的更恶心。

【问题讨论】:

  • awk '{ sed -iE ... }' -- ...错误,什么? awk 需要 awk 源代码。 sed 不是 awk 语言中的有效语句或函数。
  • 明确地说,awk 和 sed 并不是这项工作的最佳选择。最好使用支持 XML 的工具(XMLStarlet 等),或包含 XML 库的语言(Python 有很多标准库选项,ElementTree 是最好的)。
  • ...好吧,你得到一个 bash 错误而不是一个 awk 错误,因为你的单引号字符串中有一个单引号,所以它不会全部被 aw​​k 解析并继续回到 bash 正在查看的上下文中。
  • ...也就是说,“bash 或 shell 脚本”是模棱两可的。我的意思是,awk 是一种独立于 bash 的编程语言,就像 Python 一样。您可以将 Python 放入 bash 脚本中,就像将 awk 放入 bash 脚本中一样容易。如果 awk 没问题,那么您肯定不会要求 100% 纯 bash 解决方案。
  • 您真的不想“就地”替换文本。磁盘便宜。使用您想要的更改编写一个新的目录树。尝试“就地”更改文件是数据损坏的秘诀。请注意,我在引号中写了“就地”,因为像 gnu-sed 这样的工具及其错误命名的就地选项不会就地编辑文件;他们创建新文件。

标签: bash sed substitution


【解决方案1】:

设置:

mkdir -p src/{base,mod,ext}

echo 'some stuff on this line
             <ClCompile Include="Folder1\Folder1File1.cpp" />
some more stuff on this line' > src/base/base.vcxproj

echo 'some stuff on this line
          <ClCompile Include="Folder2\Folder2File1.cpp" />
some more stuff on this line' > src/mod/mod.vcxproj

echo 'some stuff on this line
           <ClCompile Include="Folder3\Folder3File1.cpp" />
some more stuff on this line' > src/ext/ext.vcxproj

为了让某些东西在我的环境中正常工作,我将中间数据放在本地文件中:

$ cat proj.dat
./src/base/base.vcxproj    <ClCompile Include="Folder1\Folder1File1.cpp" />
./src/mod/mod.vcxproj    <ClCompile Include="Folder2\Folder2File1.cpp" />
./src/ext/ext.vcxproj    <ClCompile Include="Folder3\Folder3File1.cpp" />

使用参数替换的一个想法:

while read -r fname oldstring                                # 2nd-Nth space-delimited fields go into the single variable "oldstring"
do
    oldstring="${oldstring//\\/\\\\}"                        # escape literal backslashes
    newstring="${oldstring//\/>/ Attribute=\"Value\"\/>}"    # replace /> with Attribute="Value"/>

    echo "##################### ${fname}"

    sed "s|${oldstring}|${newstring}|g" "${fname}"
done < proj.dat

注意事项:

  • sed 替换应用于文件中的所有出现
  • 如果额外的数据集导致 sed 因错误而中止,则可能需要添加额外的参数扩展来转义其他有问题的字符
  • Attribute 字符串的前面添加了一个空格,因为文字说明表明/&gt; 之前可能不存在空格(例如...file.cpp"/&gt;
  • OP 应该能够将当前的egrep | xargs | egrep | sed 传送到这个while 循环(用done 替换done &lt; proj.dat
  • 一旦 OP 对结果感到满意,-i 标志可以添加到 sed 调用以执行 ${fname} 的就地更新

生成:

##################### ./src/base/base.vcxproj
some stuff on this line
             <ClCompile Include="Folder1\Folder1File1.cpp"  Attribute="Value"/>
some more stuff on this line
##################### ./src/mod/mod.vcxproj
some stuff on this line
          <ClCompile Include="Folder2\Folder2File1.cpp"  Attribute="Value"/>
some more stuff on this line
##################### ./src/ext/ext.vcxproj
some stuff on this line
           <ClCompile Include="Folder3\Folder3File1.cpp"  Attribute="Value"/>
some more stuff on this line

【讨论】:

  • 这看起来很有前途,也更易于维护,比我在脚本上的尝试。实际上,比我所希望的还要多。明天试试!
猜你喜欢
  • 2019-06-01
  • 2017-11-24
  • 2016-12-24
  • 2019-08-07
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 2020-02-18
相关资源
最近更新 更多