我需要替换一个文件夹中的许多文件中的字符串,只有ssh访问服务器。我该怎么做呢?
当前回答
我给出了一个修复python源代码中常见shebang错误的示例。
您可以尝试grep/sed方法。下面是一个适用于GNU sed并且不会破坏git回购的程序:
$ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \
gsed -i '' -e 's/#!\/usr\/bin\/python/#!\/usr\/bin\/env python/' {}
或者你可以使用greptile:)
$ greptile -x .py -l -i -g '#!/usr/bin/env python' -r '#!/usr/bin/python' .
我刚刚测试了第一个脚本,第二个脚本应该也可以工作。小心转义字符,我认为在大多数情况下使用greptile应该更容易。当然,您可以使用sed做许多有趣的事情,为此,最好与xargs一起使用sed。
其他回答
在我发现这个问题(和答案)之前,我确实编造了自己的解决方案。 我搜索了“replace”、“few”和“xml”的不同组合,因为那是我的应用程序,但没有找到这个特定的组合。
我的问题:我有spring xml文件,其中包含测试用例的数据,其中包含复杂的对象。java源代码上的重构改变了许多类,但不适用于xml数据文件。为了保存测试用例数据,我需要更改所有xml文件中的所有类名,这些文件分布在几个目录中。同时保存原始xml文件的备份副本(尽管这不是必须的,因为版本控制可以在这里保存我)。
我正在寻找find + sed的某种组合,因为它在其他情况下也适用,但不能同时使用几个替换。
然后我找到了ask ubuntu response,它帮助我构建了我的命令行:
find -name "*.xml" -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
而且效果非常好(好吧,我的箱子有六种不同的替代品)。但请注意,它将触及当前目录下的所有*.xml文件。正因为如此,如果你要对版本控制系统负责,你可能想要先进行过滤,只将那些实际拥有你想要的字符串传递给sed;如:
find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} \; -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
当使用-i开关调用时,流编辑器确实会“inplace”修改多个文件,该开关以备份文件结尾作为参数。所以
sed -i.bak 's/foo/bar/g' *
在此文件夹中的所有文件中,将foo替换为bar,但不会下降到子文件夹中。但是,这将为目录中的每个文件生成一个新的.bak文件。 要递归地为该目录及其所有子目录中的所有文件执行此操作,您需要一个助手(如find)来遍历目录树。
find ./ -print0 | xargs -0 sed -i.bak 's/foo/bar/g' *
Find允许您进一步限制要修改的文件,通过指定进一步的参数,如Find ./ -name '*.php' -或name '*.html' -print0(如有必要)。
注意:GNU sed不需要文件结尾,sed -i 's/foo/bar/g' *也可以;FreeBSD sed要求扩展,但允许中间有一个空格,因此sed -i .bak s/foo/bar/g *可以工作。
@kev的答案很好,但只影响直接目录中的文件。下面的示例使用grep递归地查找文件。这对我来说每次都很有效。
grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @
命令分解
Grep -r:——recursive,递归地读取每个目录下的所有文件。 Grep -l:——print-with-matches,打印每个有匹配项的文件的名称,而不是打印匹配行。 Grep -i:——忽略大小写。
xargs:将STDIN转换为参数,遵循这个答案。 Xargs -i@ ~命令包含@~:一个占位符,用于~命令中特定位置的参数,@符号是一个占位符,可以被任何字符串替换。
Sed -i:就地编辑文件,不进行备份。 Sed /regexp/replace /:替换匹配regexp的字符串。 Sed s/regexp/replacement/g: global,对每个匹配进行替换,而不是仅对第一个匹配进行替换。
multiedit命令脚本
multiedit [-n PATTERN] OLDSTRING NEWSTRING
根据Kaspar的回答,我编写了一个bash脚本来接受命令行参数,并有选择地限制与模式匹配的文件名。保存在$PATH中并使其可执行,然后使用上面的命令。
剧本如下:
#!/bin/bash
_help="\n
Replace OLDSTRING with NEWSTRING recursively starting from current directory\n
multiedit [-n PATTERN] OLDSTRING NEWSTRING\n
[-n PATTERN] option limits to filenames matching PATTERN\n
Note: backslash escape special characters\n
Note: enclose STRINGS with spaces in double quotes\n
Example to limit the edit to python files:\n
multiedit -n \*.py \"OLD STRING\" NEWSTRING\n"
# ensure correct number of arguments, otherwise display help...
if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
if [ $1 == "-n" ]; then # if -n option is given:
# replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} \;
else
# replace OLDSTRING with NEWSTRING recursively in all files
find ./ -type f -exec sed -i "s/$1/$2/" {} \;
fi
为了维护我个人的英文节点,我写了一个实用程序脚本,帮助替换多对旧/新字符串,递归为一个目录下的所有文件。
新/旧字符串的多个对在一个散列映射中进行管理。
dir可以通过命令行或环境变量设置,映射是硬编码在脚本中,但如果需要,您可以修改代码从文件加载。
由于一些新特性,它需要bash 4.2。
en_standardize.sh:
#! /bin/bash
# (need bash 4.2+,)
#
# Standardize phonetic symbol of English.
#
# format:
# en_standardize.sh [<dir>]
#
# params:
# * dir
# target dir, optional,
# if not specified then use environment variable "$node_dir_en",
# if both not provided, then will not execute,
# *
#
paramCount=$#
# figure target dir,
if [ $paramCount -ge 1 ]; then # dir specified
echo -e "dir specified (in command):\n\t$1\n"
targetDir=$1
elif [[ -v node_dir_en ]]; then # environable set,
echo -e "dir specified (in environment vairable):\n\t$node_dir_en\n"
targetDir=$node_dir_en
else # environable not set,
echo "dir not specified, won't execute"
exit
fi
# check whether dir exists,
if [ -d $targetDir ]; then
cd $targetDir
else
echo -e "invalid dir location:\n\t$targetDir\n"
exit
fi
# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")
# print item maps,
echo 'maps:'
for key in "${!itemMap[@]}"; do
echo -e "\t$key\t->\t${itemMap[$key]}"
done
echo -e '\n'
# do replace,
for key in "${!itemMap[@]}"; do
grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
done
echo -e "\nDone."
exit