我想合并到多个子目录中的1个目录文件中。
除了在扩展后添加随机字符串外,以下内容非常接近;我希望它在扩展之前:
find . -type f -iname "[a-z,0-9]*" -exec bash -c 'mv -v "$0" "./$( mktemp "$( basename "$0" ).XXX" )"' '{}' \;
我搜索过其他几十篇文章,但没有解决我的具体情况:
find -type f
find -type f -iname "[a-z,0-9]*"
答案 0 :(得分:0)
这基本上使用了您使用的相同命令,但我向mktemp
提供了一个模板,以便XXX
模式出现在后缀之前。使用GNU sed
:
find . -type f -iname "[a-z,0-9]*" -exec bash -c 'mv -v "$1" "./$(mktemp -u "$(basename "$1" | sed -E -e '\''s/\.([^.]+)$/.XXX.\1/'\'' -e '\''/XXX/ !s/$/.XXX/'\'')" )"' _ '{}' \;
上面的关键添加是使用sed
在文件名中的后缀之前插入XXX
:
sed -E -e 's/\.([^.]+)$/.XXX.\1/' -e '/XXX/ !s/$/.XXX/'
这有两个命令。第一个将.XXX
放在扩展名之前。仅当文件名没有扩展名时才运行第二个命令,在这种情况下,它会将.XXX
添加到文件名的末尾。
在第一个命令中,源正则表达式由两部分组成。第一个是\.
,它匹配一个句号。第二个是([^.]+)$
,它将扩展名捕获到第1组。替换替换为.XXX.\1
,其中\1
是sed
表示第1组,在我们的例子中是文件的扩展名。
在OSX下,mktemp
没有用,因为它只支持XXX部分跟踪的模板。作为解决方法,我们可以使用生成非重叠文件名的bash脚本:
#!/bin/bash
find . -type f -iname "[a-z,0-9]*" -print0 |
while IFS= read -r -d '' fname
do
new=$(basename "$fname")
[ "$fname" = "./$new" ] && continue
[ "$new" = .DS_store ] && continue
name=${new%.*}
ext=${new#"$name"}
n=0
new=$(printf '%s.%03i%s' "$name" "$n" "$ext")
while [ -f "$new" ]
do
n=$(($n + 1))
new=$(printf '%s.%03i%s' "$name" "$n" "$ext")
done
mv -v "$fname" "$new"
done
以上使用find
命令获取文件名。选项-print0
用于确保它适用于困难的文件名。 while
循环将这些文件名逐个读入变量fname
。 fname
包含源文件的完整路径。然后,没有路径的文件名存储在new
中。然后执行两次检查。如果源文件已在当前目录中,则脚本将继续执行下一个循环。同样,如果文件名为.DS_Store
,则也会跳过它。 (给定的find
命令已经跳过这些文件。这一行只是为了将来的灵活性。)接下来,文件名分为两部分:name
和ext
,扩展名。 ext
包括领先时期。接下来,循环检查格式为name.NNN.ext的文件,并在第一个尚未存在的文件处停止。源文件将移动到该名称的文件。
在上面的GNU命令中引用很复杂。 bash -c
的参数需要使用单引号来防止调用bash执行过早变量替换。此外,当由bash子shell执行时,sed
命令需要在单引号中,以防止历史扩展干扰!
命令中的否定sed
的使用。
OSX(BSD)sed
不支持将命令与分号组合在一起。因此,每个命令都通过单独的sed
选项提供给-e
。
OSX(BSD)sed
似乎与{GNU +
区别对待sed
。使用-E
(扩展正则表达式)选项时,这种不兼容性似乎消失了。 (相应的GNU选项为-r
但是,作为未记录的兼容性功能,GNU sed
也支持-E
。