我有以下结构
在
-1.2.3.4
--1.2.3.4
---1.2.3.4-->1.2.3.4.file
在
-5.6.7.8
--5.6.7.8
---5.6.7.8-->5.6.7.8.file
...
我想使用bash脚本在特定目录/ home中搜索特定字符串的文件和子目录名称,并在找到搜索字符串时(在文件或中) name,file content,subdirectories name),将字符串(old1)替换为new string(new1),将old2替换为new2 .... oldn with newn。
#!/bin/bash
FILES_PATH="/home/pons/test"
FILES=$(find $FILES_PATH -type f -name "*")
for FILE in $FILES; do
sed -i 's/old1/new1/ ; .... ; s/oldn/newn/' $FILE
done
e.g。在所有
中将fadi替换为服务器[pons@server1 ~]$ pwd
/home/pons
[pons@server1 ~]$ cd test
[pons@server1 test]$ ls -al
total 12
drwxrwxr-x 3 pons pons 4096 Sep 29 09:50 .
drwx------. 26 pons pons 4096 Sep 29 10:21 ..
drwxrwxr-x 2 pons pons 4096 Sep 29 09:47 fadi
[pons@server1 test]$ cd fadi/
[pons@server1 fadi]$ pwd
/home/pons/test/fadi
[pons@server1 fadi]$ ls -al
total 12<br/>
drwxrwxr-x 2 pons pons 4096 Sep 29 09:47 .
drwxrwxr-x 3 pons pons 4096 Sep 29 09:50 ..
-rw-rw-r-- 1 pons pons 27 Sep 29 09:47 xxxfadixx.txt
[pons@server1 fadi]$ cat xxxfadixx.txt
xxxxxxxxxxxxfadixxxxxxxxxx
[pons@server1 fadi]$
我可以在一个文件中添加多个字符串并执行替换
e.g。创建一个指定过滤例程的文件:
filter.txt
S / old1 /名new1 /
S / old2 / NEW2 /
S / old3 / NEW3 /
......
S / oldn / newn /
然后在脚本中读取该文件
#!/bin/bash
ROOT_DIR="$HOME/root" # your target dir
P_FROM="1.2.3.4"
P_TO="5.6.7.8"
# for each directory, starting from deepest first
while IFS= read -r -d $'\0' DIR_NAME; do
cd "$DIR_NAME" # go to target dir
rename $P_FROM $P_TO * # rename files
find . -type f -maxdepth 1 -exec sed -i "s/$P_FROM/$P_TO/g" {} \;
cd - > /dev/null # back to original dir. Suppress stdout
done < <(find $ROOT_DIR -type d -depth -print0) # get only dirs
Shawn Chin的上述脚本完美地替换/重命名字符串,如果我要修改多个字符串并将这些字符串分配到文件中,如何修改它
filter.txt
s / old1 / new1 /
s / old2 / new2 /
S / old3 / NEW3 /
......
s / oldn / newn /
#!/bin/bash
ROOT_DIR="$HOME/test" # your target dir
FILTER_FILE="$HOME/filter.sed" # the sed script for renaming
# custom rename function that uses $FILTER_FILE (via sed)
function rename_using_filter {
CURRENT_NAME="$1"
NEW_NAME="$(echo $1 | sed -f $FILTER_FILE)" # derive new name
if [ "$CURRENT_NAME" != "$NEW_NAME" ]; then # rename if diff
mv "$CURRENT_NAME" "$NEW_NAME"
fi
}
# for each directory, starting from deepest first
while IFS= read -r -d $'\0' DIR_NAME; do
cd "$DIR_NAME" # go to target dir
# for each file/dir at this level
while IFS= read -r -d $'\0' FILE_NAME; do
rename_using_filter "$FILE_NAME" # rename it
if [ -f "$FILE_NAME" ]; then # if it's a file
sed -i -f "$FILTER_FILE" "$FILE_NAME"; # replace content
fi
done < <(find . -maxdepth 1 -print0)
cd - > /dev/null # back to original dir. Suppress stdout
done < <(find $ROOT_DIR -depth -type d -print0) # get only dirs
我使用filter.sed完成了上述操作,它可以替换名称文件/目录,但没有替换文件的内容......请你检查一下。
[pons@server1 test]$ find .
.
./boy_dir
./boy_dir/boy.txt
./papa
./papa/papa_new
./papa/papa_new/papa.txt
./boy
[pons@server1 test]$ cat ./papa/papa_new/papa.txt
xxxxxxxxxxxxxxxxxxxxpapaxxxxxxxxxxxx
[pons@server1 test]$ cat ./boy_dir/boy.txt
xxxxxxxxxxxxboyxxxxxxxxx
[pons@server1 test]$ cd ..
[pons@server1 ~]$ ./replace.sh
[pons@server1 ~]$ cd test
[pons@server1 test]$ find .
.
./girl_dir
./girl_dir/girl.txt
./girl
./mama
./mama/mama_new
./mama/mama_new/mama.txt
[pons@server1 test]$ cat ./mama/mama_new/mama.txt
xxxxxxxxxxxxxxxxxxxxpapaxxxxxxxxxxxx
[pons@server1 test]$ cat ./girl_dir/girl.txt
xxxxxxxxxxxxboyxxxxxxxxx
[pons@server1 test]$
I used this filter
[pons@server1 ~]$ cat filter.sed
s/papa/mama/g;
s/boy/girl/g;
s/old3/new3/g;
s/old3/new4/g;
s/oldn/newn/g;
"filter.sed" 5L, 95C written
[pons@server1 ~]$ ./replace.sh
[pons@server1 ~]$ cd test
[pons@server1 test]$ find .
.
./A.B.C.D.E_
./a.b.c.d.e
./a.b.c.d.e/a.b.c.d.e
./a.b.c.d.e_
./A.B.C.D.E
./A.B.C.D.E/A.B.C.D.E
[pons@server1 test]$ cat ./A.B.C.D.E/A.B.C.D.E
1.2.3.4.5
[pons@server1 test]$ cat ./a.b.c.d.e/a.b.c.d.e
6.7.8.9.0
[pons@server1 test]$ cat ./A.B.C.D.E_
1.2.3.4.5
[pons@server1 test]$ cat ./a.b.c.d.e_
6.7.8.9.0
[pons@server1 test]$ cd ..
[pons@server1 ~]$ ./replace.sh
[pons@server1 ~]$ cd test
[pons@server1 test]$ find .
.
./A.B.C.D.E_
./a.b.c.d.e
./a.b.c.d.e/a.b.c.d.e
./a.b.c.d.e_
./A.B.C.D.E
./A.B.C.D.E/A.B.C.D.E
[pons@server1 test]$ cat ./A.B.C.D.E/A.B.C.D.E
A.B.C.D.E
[pons@server1 test]$ cat ./a.b.c.d.e/a.b.c.d.e
a.b.c.d.e
[pons@server1 test]$ cat ./A.B.C.D.E_
A.B.C.D.E
[pons@server1 test]$ cat ./a.b.c.d.e_
a.b.c.d.e
[pons@server1 test]$
[pons@server1 ~]$ vi filter.sed
s/1.2.3.4.5/A.B.C.D.E/g;
s/6.7.8.9.0/a.b.c.d.e/g;
s/old3/new3/g;
s/old3/new4/g;
s/oldn/newn/g;
〜
答案 0 :(得分:3)
您可以为每次替换运行rename
(是的,这是现有程序)。例如,在Bash&gt; = 4.0中,这里是如何从当前目录中的所有文件中删除“.bak”扩展名:
shopt -s globstar # Enable recursive globs with **
rename 's/\.bak$//' **.bak
另一个例子,做几次替换:
$ touch /tmp/foobarbaz
$ rename 's/foo/bar/g;s/bar/baz/g' /tmp/foobarbaz
$ ls /tmp
bazbazbaz
对于您的特定替换,这应该有效:
rename 's/old1/new1/ ; .... ; s/oldn/newn/' /home/pons/test/**
答案 1 :(得分:3)
从我收集的内容中,您想要遍历目录结构并执行以下操作:
我将如何做到这一点。
警告:答案很长。如果您赶时间,请直接跳到最后一节以获取完整的脚本。也可能是过度工程师(应该有一种更简单的方法来实现这一点,真的......)。
假设以下文件结构(基于您的第一个示例):
[me@home]$ find .
.
./1.2.3.4
./1.2.3.4/1.2.3.4
./1.2.3.4/1.2.3.4/1.2.3.4
./1.2.3.4/1.2.3.4/1.2.3.4/1.2.3.4.file
./1.2.3.4/1.2.3.4/1.2.3.4/1.2.3.4-2.file
[me@home]$ cat ./1.2.3.4/1.2.3.4/1.2.3.4/1.2.3.4.file
IP: 1.2.3.4
[me@home]$ cat ./1.2.3.4/1.2.3.4/1.2.3.4/1.2.3.4-2.file
IP: 1.2.3.4 (also)
这很容易。只需使用find
和sed
[me@home]$ find . -type f -exec sed -i "s/1\.2\.3\.4/5.6.7.8/g" {} \;
[me@home]$ cat ./1.2.3.4/1.2.3.4/1.2.3.4/1.2.3.4.file
IP: 5.6.7.8
[me@home]$ cat ./1.2.3.4/1.2.3.4/1.2.3.4/1.2.3.4-2.file
IP: 5.6.7.8 (also)
我首先假设它会像find . -exec rename 1.2.3.4 5.6.7.8 {} \;
一样简单。
不幸的是,这不起作用。使用echo
添加命令会显示无法使用的原因。
[me@home]$ find . -exec echo rename 1.2.3.4 5.6.7.8 {} \;
rename 1.2.3.4 5.6.7.8 .
rename 1.2.3.4 5.6.7.8 ./1.2.3.4
rename 1.2.3.4 5.6.7.8 ./1.2.3.4/1.2.3.4
rename 1.2.3.4 5.6.7.8 ./1.2.3.4/1.2.3.4/1.2.3.4
rename 1.2.3.4 5.6.7.8 ./1.2.3.4/1.2.3.4/1.2.3.4/1.2.3.4.file
rename 1.2.3.4 5.6.7.8 ./1.2.3.4/1.2.3.4/1.2.3.4/1.2.3.4-2.file
顶级目录(./1.2.3.4
)的重命名将成功,但是一旦我们下一级重命名./1.2.3.4/1.2.3.4
,这将失败,因为父目录已经被重命名。
我们可以颠倒遍历的顺序(使用-depth
),但这只能解决问题的一半,因为我们需要每个步骤只重命名最低级别的匹配而不是整个路径。
我们必须做的是从最低级别开始重命名过程并向上工作。这是一种方法:
#!/bin/bash
ROOT_DIR="$HOME/root" # your target dir
P_FROM="1.2.3.4"
P_TO="5.6.7.8"
# for each directory, starting from deepest first
while IFS= read -r -d $'\0' DIR_NAME; do
(cd "$DIR_NAME" && rename $P_FROM $P_TO *)
done < <(find $ROOT_DIR -depth -type d -print0) # get only dirs
外观复杂的while
循环允许我们处理包含空格的文件名(有关详细信息,请参阅http://mywiki.wooledge.org/BashFAQ/020)。
此脚本假定存在rename
命令。
在我们的测试目录上运行该脚本会产生以下结果:
[me@home]$ bash renamer.sh
[me@home]$ find .
.
./5.6.7.8
./5.6.7.8/5.6.7.8
./5.6.7.8/5.6.7.8/5.6.7.8
./5.6.7.8/5.6.7.8/5.6.7.8/5.6.7.8.file
./5.6.7.8/5.6.7.8/5.6.7.8/5.6.7.8-2.file
我们可以通过简单地将find
+ sed
调用添加到循环中,将上述两个步骤合并为一个解决方案。
#!/bin/bash
ROOT_DIR="$HOME/root" # your target dir
P_FROM="1.2.3.4"
P_TO="5.6.7.8"
# for each directory, starting from deepest first
while IFS= read -r -d $'\0' DIR_NAME; do
cd "$DIR_NAME" # go to target dir
rename $P_FROM $P_TO * # rename files
find . -type f -maxdepth 1 -exec sed -i "s/$P_FROM/$P_TO/g" {} \;
cd - > /dev/null # back to original dir. Suppress stdout
done < <(find $ROOT_DIR -depth -type d -print0) # get only dirs
假设您有一个sed文件filter.sed
:
s/old1/new1/g;
s/old2/new2/g;
s/old3/new3/g;
s/old3/new4/g;
s/oldn/newn/g;
只要filter.sed
是有效的sed文件,替换文件的内容就很简单。我们只使用-f
选项:
[me@home]$ cat somefile.txt
hello old1 old2 old3 world
[me@home]$ sed -i -f filter.sed somefile.txt
[me@home]$ cat somefile.txt
hello new1 new2 new3 world
然而,重命名文件/目录却比较棘手(除非你使用仅在基于debian的系统上可用的Perl-based rename命令。这样你就可以按照rename "$(cat filter.sed)" *
的方式做一些事情。
仅使用标准mv
命令,这是一种方法。我怀疑这可能不是最有效的方法,但经过简单测试后,似乎工作。 YMMV。
#!/bin/bash
ROOT_DIR="$HOME/root" # your target dir
FILTER_FILE="$HOME/filter.sed" # the sed script for renaming
# custom rename function that uses $FILTER_FILE (via sed)
function rename_using_filter {
CURRENT_NAME="$1"
NEW_NAME="$(echo $1 | sed -f $FILTER_FILE)" # derive new name
if [ "$CURRENT_NAME" != "$NEW_NAME" ]; then # rename if diff
mv "$CURRENT_NAME" "$NEW_NAME"
fi
}
# for each directory, starting from deepest first
while IFS= read -r -d $'\0' DIR_NAME; do
cd "$DIR_NAME" # go to target dir
# for each file/dir at this level
while IFS= read -r -d $'\0' FILE_NAME; do
if [ -f "$FILE_NAME" ]; then # if it's a file
sed -i -f "$FILTER_FILE" "$FILE_NAME" # replace content
fi
rename_using_filter "$FILE_NAME" # rename it
done < <(find . -maxdepth 1 -print0)
cd - > /dev/null # back to original dir. Suppress stdout
done < <(find $ROOT_DIR -depth -type d -print0) # get only dirs
以下是正在运行的脚本:
[me@home]$ find .
.
./old1_old2
./old1_old2/old3_old4
./old1_old2/old3_old4/some-oldn.txt
./renamr.sh
[me@home]$ cat old1_old2/old3_old4/some-oldn.txt
hello old1 old2 old3 old4 oldn world
[me@home]$ bash renamr.sh
[me@home]$ find .
.
./.renamr.sh.swp
./new1_new2
./new1_new2/new3_new4
./new1_new2/new3_new4/some-newn.txt
./renamr.sh
[me@home]$ cat ./new1_new2/new3_new4/some-newn.txt
hello new1 new2 new3 new4 newn world
答案 2 :(得分:0)
我并不完全明白你想要达到的目标,但我认为这至少会修复你的代码:
mv "$FILE" "$(echo "$FILE" | sed -i 's/old1/new1/ ; .... ; s/oldn/newn/')"
答案 3 :(得分:0)
所以你想做类似的事情:
find $FILES_PATH -type f | while read FILE; do
NEWNAME="$(printf '%s\n' "$FILE" | sed 's/old1/new1/ ; .... ; s/oldn/newn/')"
mv "$FILE" "$NEWNAME"
done
注意,对于sed,不想要-i
,stdin无法就地修改。
通过使用sed输出两个名称可以提高效率,因此您可以在整个输出上只运行一次,但需要使用保留空间的多个替换。它就像:
find $FILES_PATH -type f | sed 'h;s/old1/new1/;...;s/oldn/newn/;x;G' | while read OLD && read NEW; do
mv "$OLD" "$NEW"
done