OSX终端:在OSX终端通过层次
问题描述:
重命名文件,我尽量扁平化文件夹层次结构,但里面的文件被命名为相同的,我要的文件名与昔日的文件夹名称扩展:OSX终端:在OSX终端通过层次
我会喜欢从这里得到:
/dirA1/dirB1/file1.ext
/dirA1/dirB2/file1.ext
...
/dirA2/dirB1/file1.ext
...
到
/file1-dirA1-dirB1.ext
/file1-dirA1-dirB2.ext
...
/file1-dirA2-dirB1.ext
...
我试图用扁平(https://unix.stackexchange.com/questions/52814)合并重命名(Batch renaming files in OSX terminal),但还没有运气...
我会以“find”开头吗?但我怎样才能将目录名称传递给“mv”?
非常感谢您提前!
答
一个安全的,但不是很快速的方法是使用find
与-exec
,每个文件传递到您进行更换,这样的shell命令:
(cd /path; find . -type f -exec sh -c 'newname=${1:2}; filename=${newname##*/}; dirname=${newname%/*}; newname=$filename-${dirname//\//-}; echo mv "$1" "$newname"' -- {} \;)
的原因cd /path
代替find /path
的目的是为了让我们可以使用该基本目录中的相对路径,这可以更轻松地创建拼合文件名。
比特的元素的更多的解释:
-
-exec sh -c '...' -- {}
执行sh
,运行在-c '...'
指定的命令。{}
是find
找到的路径。-- ...
语法是将参数作为位置参数传递给脚本,可通过$1
,$2
等访问。 -
newname=${1:2}
需要$1
,第一个位置参数,并从中提取一个子字符串,跳过前两个字符。我这样做是因为find .
的输出将具有从./
开始的路径,并且我们希望在将/
替换为-
之前删除该路径。 -
filename=${newname##*/}
提取路径的文件名部分,直到最后/
-
dirname=${newname%/*}
去除一切从一开始提取路径的目录名称部分,通过移除最后/
及其之后的所有 -
${dirname//\//-}
替换所有出现/
与-
。如果你想删除/
而不是取代的,那么你可以写${dirname//\//}
这项技术是安全的,因为它会在文件名中的任何特殊字符的工作。这很慢,因为它为每个文件运行sh
。
这一步原始文件的空目录仍将之后,你可能想将其删除:
find /path -type d -empty -delete
答
可以安全地通过find … -print0
输出供给到while read
处理这个不为每个文件执行sh
环,如:
while IFS= read -r -d '' old; do # read filenames into $old
# -d '' : read until \0
# -r : no backslash escapes
# IFS= : no trimming whitespace
if [[ $old =~ (.*)/(.*)\.(.*) ]]; then # regex with capture groups:
# 1 2 3
set -- "${BASH_REMATCH[@]}"
mv "$old" "${2}-${1//\/}.${3}"
fi
done < <(find . -type f -print 0)
上述set -- "${BASH_REMATCH[@]}"
的目的仅仅是移动包含正则表达式匹配的数组的成员将其分组为位置参数($1
,$2
等)以便易读。
谢谢!将尽快尝试。 - 你能指导我(或者指点一下)newname = $ {1:2}和newname // \/- 什么都正确吗? – Frank
我增加了更多解释,并修复了一个重要的错字。替换应该是'newname = $ {newname // \ // - }',而不是'newname = $ {newname // \/- }',因为它在编辑之前。 – janos
谢谢,Janos!实际上,我希望在原始文件名后加上/后面的目录名称。如何告诉mv追加参数而不是预先添加它们? (另外,我怎么能完全删除/字符,而不是用 - 代替 - ?我不能用“无字符”代替,对吧?) – Frank