在 Bash 中使用单行重命名目录及其子目录
Posted
技术标签:
【中文标题】在 Bash 中使用单行重命名目录及其子目录【英文标题】:Rename a directory and its sub-directory with a one liner in Bash 【发布时间】:2019-10-13 10:18:42 【问题描述】:我有一个我反编译的 android 应用程序,我正在编写一个脚本来重命名目录。
反编译文件夹里面是路径:
smali/com/FOLDER/SUB-FOLDER
我想将现有文件夹重命名为:
smali/com/NEW-NAME/SUB-NEW-NAME
我在想我可以使用 mv 命令,但在手册页中挖掘它似乎无法完成。我正在尝试这个:
mv smali/com/FOLDER smali/com/FOLDER/SUB-FOLDER -t smali/com/NEW-NAME/SUB-NEW-NAME
这仅适用于将多个文件移动到单个目的地。
编辑:
现有文件夹:
smali/com/FOLDER/SUB-FOLDER
我正在尝试重命名这两个文件夹,同时保留每个文件夹的内容。
一个一个地重命名每个文件夹的例子是:
mv smali/com/FOLDER/ smali/com/NEW-NAME
mv smali/com/NEW-NAME/SUB-FOLDER smali/com/NEW-NAME/SUB-NEW-NAME
【问题讨论】:
unix.stackexchange.com/q/189253/273492 Is there a way to make mv create the directory to be moved to if it doesn't exist?的可能重复 文件夹已经存在并且每个文件夹都有内容我只想重命名文件夹 “单线”?除非您有令人信服的技术原因,否则我认为该限制将这个问题排除在本网站所需的“实用”标准之外。将简洁性优先于正确性或可读性适用于打代码高尔夫(如 Code Golf 所做的那样),而不是实际用例。 "只想重命名文件夹" -- 那么,smali/com/FOLDER/OTHER-NAME
是否应该更改为 smali/com/NEW-NAME/OTHER-NAME
?您说的是“文件夹”,复数形式,如果您将 FOLDER
重命名为 NEW-NAME
,则预计会出现 OTHER-NAME
。如果你希望FOLDER
和NEW-NAME
同时存在(所以OTHER-NAME
仍然可以留在FOLDER
),那么你不能重命名existing OTHER-NAME
,你必须创建一个 new OTHER-NAME
.
【参考方案1】:
这是一个微不足道的单行;例如:
rename_pieces smali/com/FOLDER/SUB-FOLDER smali/com/NEW/SUBNEW
...当然,假设您首先运行适当的函数定义。如果您只想重命名SUB-FOLDER
,则在创建NEW-NAME
后如果它不存在,则如下所示:
rename_pieces()
local old=$1 new=$2
[[ $new = */* ]] && mkdir -p -- "$new%/*"
mv -T -- "$old" "$new"
...与任何其他解释相比,这更可能是您真正想要的行为,只要它留下FOLDER
的内容 SUB-FOLDER
仅以其原始名称。
相比之下,如果您真的想要重命名这两个目录,那就更有趣了。如果我们保证源和目标的深度相同,则可能如下所示:
log_or_run()
if [[ $log_only ]]; then # just log what we would run
printf '%q ' "$@" >&2 # use printf %q to generate a safely-escaped version
printf '\n' >&2 # ...and terminate with a newline.
else
"$@" # actually run the command
fi
rename_pieces()
local old=$1 new=$2 common
while [[ $old%%/* = "$new%%/*" ]]; do
common+=/"$old%%/*"
old=$old#*/; new=$new#*/
done
while [[ $old && $new ]]; do
log_or_run mv -T -- "$common#//$old%%/*" "$common#//$new%%/*"; echo
common+=/"$new%%/*"
[[ $old = */* && $new = */* ]] || return
old=$old#*/; new=$new#*/
done
之后:
log_only=1 rename_pieces smali/com/FOLDER/SUB-FOLDER smali/com/NEW/SUBNEW
...输出时发射:
mv -T -- smali/com/FOLDER smali/com/NEW
mv -T -- smali/com/NEW/SUB-FOLDER smali/com/NEW/SUBNEW
...在没有log_only=1
的情况下执行同样的操作实际上会运行这些命令。
【讨论】:
【参考方案2】:想不出更优雅的方法,但以下应该可以:
find . -path '.*com/FOLDER/SUBFOLDER' | awk 'new_dir=$1; sub("/FOLDER/SUBFOLDER","/NEW/SUBNEW", $new_dir); printf("mkdir -p %s\nmv %s %s\n", $new_dir, $1, $new_dir);' > cmds
这会在cmds
文件中获取所有必需的mv
命令。验证它是否正确,然后使用sh cmds
执行。虽然这不是最有趣的,但在处理重要数据时我喜欢这种方法,因为验证您将要运行的内容总是更好。
编辑:修复了 cmets 中指出的脚本不存在时不创建目录的问题。
【讨论】:
从安全角度来看,这是一个可怕的想法;它 需要 进行验证,因为您正在生成一个字符串,并且该字符串可能具有被解析为语法的内容。相比之下,常规的 shell 扩展 不 被解析为语法,因此无论它们的内容是什么,它们都可以安全运行(至少,在它们不能运行完全不相关的命令、重定向的范围内是安全的)等)。 例如:mv -- "$old" "$new"
可以被信任除了mv
之外永远不会运行任何命令,即使new=$'$(rm -rf ~)\'$(rm -rf ~)\''
也是如此。但是,一旦您使用awk
生成该mv
命令并通过shell 运行它,这些保证就会完全消失,因为您将数据解析为代码,否则它将永远无法成为数据以外的任何东西。
此外,这实际上不起作用。它生成一个命令mv ./smali/com/FOLDER/SUBFOLDER ./smali/com/NEW/SUBNEW
,但该命令将失败,因为smali/com/NEW
不存在。
@CharlesDuffy 好吧,这就是为什么您需要验证 cmds
文件中生成的内容。我同意生成子目录的失误,但这应该很容易通过前缀 mv 来解决。你的答案肯定是更“正确”的答案,但如果我必须做一些快速、临时和肮脏的事情,这就是我会做的。
Err -- 如果你mkdir new_dir
,那么mv something new-dir
将创建new-dir/something
;所以在这种情况下,你最终会得到/NEW/SUBNEW/FOLDER
而不是/NEW/SUBNEW
。此外,在 cmets 中,OP 非常清楚这个问题(诚然,为了清楚起见,我花了很多时间试图按下它们)他们确实想要重命名 both 目录,这样@ 987654338@ 变为 /NEW-NAME/something-else
。如果不是这样,这个问题将是重复的。【参考方案3】:
好吧,我会咬人的……
如果你想要的只是一条线,你可以随时使用AND_IF
命令&&
将多条线合二为一。
mv ./FOLDER ./NEW-NAME && mv ./NEW-NAME/SUB-FOLDER ./NEW-NAME/SUB-NEW-NAME
这增加了子文件夹重命名只有在父重命名成功时才会发生的附加效果。
但是,首先移动子文件夹不是更有意义吗?让我们为 from 标签和 $t
分配两个变量 $f
。这些可以放在一条线上,但是当我们想再次更改内容时,这种方式更容易。
f=FOLDER; t=NEW-NAME;
mv "./$f/SUB-$f" "./$f/SUB-$t" && mv "./$f" "./$t"
所有剩余的例子都假设设置了 from 和 to 变量。
让我们将$p
分配给父查找并将$s
分配给所有匹配的子文件夹,以确保它们都存在。这个例子和上面所有的例子仍然是纯 BASH 解决方案。
p=(./*$f); [[ -d $p ]] && for s in $p/*$f; do mv "$s" "$s%$f$t"; done && mv "$p" "$p%$f$t"
要考虑的另一种选择是使用find
,就像在这一个班轮中一样。
find . -d -type d -name "*$f" -execdir sh -c 'mv -- "$0" "$0%$1$2"' "" "$f" "$t" \;
这只会重命名匹配的文件夹,但它会在所有子文件夹中查找匹配项,使用 -maxdepth 2
限制遍历深度或添加 -mindepth
如果使用不同的起点进行校准。
开心!
【讨论】:
以上是关于在 Bash 中使用单行重命名目录及其子目录的主要内容,如果未能解决你的问题,请参考以下文章
在 bash 脚本中获取祖父目录 - 为路径中的目录重命名文件