bash/sed/awk: 替换任意文件中的任意子串

bash/sed/awk: replace arbitrary substring in arbitrary files

更新:

我试过 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/(,)/ Attribute="Value"/g' }'

哪些错误出

bash: syntax error near unexpected token `('

我已经尝试过使用 shell 脚本的解决方案,但也没有用,我不知道是否以及如何解决上述错误消息。我对任何解决方案都持开放态度,只要它是 bash 内的 运行,甚至可以比我想出的更严重。

设置:

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 字符串的前面添加了一个 space,因为文本描述表明 /> 之前可能不存在 space(例如,...file.cpp"/>)
  • OP 应该能够将当前 egrep | xargs | egrep | sed 传送到此 while 循环(将 done < proj.dat 替换为 done
  • 一旦 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