我有一个文件集列表,其中有很多扩展名,但名称唯一。
filename-1.foo.001
...
filename-1.foo.020
filename-1.foo.baz
filename-1.foo.bar1-2.baz
...
filename-1.foo.bar7-8.baz
another_filename.foo.001
another_filename.foo.002
...
another_filename.foo.009
another_filename.foo.baz
another_filename.foo.bar1-2.baz
another_filename.foo.bar3-4.baz
another_filename.foo.bar4-5.baz
another_filename.foo.bar7-8.baz
yet.a.different.file.name.foo.001
yet.a.different.file.name.foo.002
...
yet.a.different.file.name.foo.287
yet.a.different.file.name.foo.baz
yet.a.different.file.name.foo.bar1-2.baz
yet.a.different.file.name.foo.bar3-4.baz
yet.a.different.file.name.foo.bar4-5.baz
yet.a.different.file.name.foo.bar7-8.baz
moreFileNaming.foo.001
...
moreFileNaming.foo.009
moreFileNaming.foo.baz
moreFileNaming.foo.bar1-2.baz
moreFileNaming.foo.bar3-4.baz
moreFileNaming.foo.bar4-5.baz
moreFileNaming.foo.bar7-8.baz
我想使用 的输出来重命名它们,openssl rand -hex 8
以获得每组的随机文件名,如下所示:
9874f7187c914ea9.foo.001
...
9874f7187c914ea9.foo.020
9874f7187c914ea9.foo.baz
9874f7187c914ea9.foo.bar1-2.baz
...
9874f7187c914ea9.foo.bar7-8.baz
2f54a0b6528e3927.foo.001
2f54a0b6528e3927.foo.002
...
2f54a0b6528e3927.foo.009
2f54a0b6528e3927.foo.baz
2f54a0b6528e3927.foo.bar1-2.baz
2f54a0b6528e3927.foo.bar3-4.baz
2f54a0b6528e3927.foo.bar4-5.baz
2f54a0b6528e3927.foo.bar7-8.baz
71ad0aa90148b2f5.foo.001
71ad0aa90148b2f5.foo.002
...
71ad0aa90148b2f5.foo.287
71ad0aa90148b2f5.foo.baz
71ad0aa90148b2f5.foo.bar1-2.baz
71ad0aa90148b2f5.foo.bar3-4.baz
71ad0aa90148b2f5.foo.bar4-5.baz
71ad0aa90148b2f5.foo.bar7-8.baz
3721323156e921b5.foo.001
...
3721323156e921b5.foo.009
3721323156e921b5.foo.baz
3721323156e921b5.foo.bar1-2.baz
3721323156e921b5.foo.bar3-4.baz
3721323156e921b5.foo.bar4-5.baz
3721323156e921b5.foo.bar7-8.baz
我已经尝试过for name (*.(<->|baz|bar<->.baz) result=$(openssl rand -hex 16) && mv $name $result
(这可能无法正常工作,因为它是多次迭代前的),但是当它起作用时,它会给出每个文件一个随机名称,我只是希望每组都保留相同的名称,只是随机且大小相同。 Sha1sum 或任何其他方法也可以。
我该如何实现这个目标?特别是对于文件.foo.bar-*.baz?
如果我们删除 foo
3721323156e921b5.001
...
3721323156e921b5.009
3721323156e921b5.baz
3721323156e921b5.bar1-2.baz
3721323156e921b5.bar3-4.baz
3721323156e921b5.bar4-5.baz
3721323156e921b5.bar7-8.baz
也会好的。还有一些问题:
- 如何从文件开头到 .foo 定位?
- 如何循环创建的变量,例如
result=$(openssl rand -hex 8)
以便在重命名中使用它,并且仅当一组完成时,才能再次分配它以循环它直到下一组完成等?
谢谢!
答案1
这个问题有几个部分:
- 将每个文件名分解为基本部分和扩展名。
- 对每个名称的基本部分应用一致的转换。
- 根据所选的基础部件转换重命名文件,保留扩展名。
1. 分解文件名
从您的示例名称中尚不完全清楚您认为文件名的基本部分是什么。分隔符显然是一个点,但在像这样的示例中yet.a.different.file.name.foo.bar1-2.baz
,哪个点?您提到使用 的尝试*.(<->|baz|bar<->.baz)
,它不会将foo
或bar1-2
视为扩展。允许它们作为扩展的一个调整是.(foo|<->|baz|bar<->(|-<->).baz)
。然后你可以按如下方式破坏文件名$f
:
setopt extended_glob
base=${f%%(.(<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
或者,如果可以将基定义为第一个之前的所有内容(并且不包括第一个).foo.
,则分解会更简单:
base=${f%*.foo.*}; extensions=${f#$base}
2. 应用一致的转换
如果你想应用确定性变换,你只需每次重新计算即可。例如,您可以通过使用密钥获取名称的 MAC(每次使用相同的密钥)来获得伪随机结果。
secret=$(openssl rand -hex 32)
for … # Loop over the files as per (3.), set $base and $extensions as per (1.)
new_base=${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}
ps
(注意:如果其他用户在运行时运行,则秘密密钥将对其他用户可见openssl
。我认为这在您的情况下不是问题,但未来的读者应该注意这一点。)
如果您想应用随机变换,您需要记住每个碱基映射到什么。有两种方法可以做到这一点:
- 您可以按碱基对文件进行分组,然后一次处理一个碱基。
- 您可以逐个处理文件,但请记住每个碱基映射到什么,并且仅在尚未看到碱基时才生成新映射。
第二种方法比较简单,第一种方法没有什么特别的优势,所以我只展示第二种方法。
建立一个关联数组将碱基映射到新碱基。
typeset -A mapping
mapping=()
for … # Loop over the files as per (3.), set $base and $extensions as per (1.)
if ((!$+mapping[$base])); then
mapping[$base]=$(openssl rand -hex 8)
fi
new_base=$mapping[$base]
3. 重命名文件
Zsh 附带了一个非常有用的工具来重命名文件:zmv
。您想要执行的转换非常复杂,zmv 不会使它变得微不足道:文件名分解和转换都需要额外的工作。即使在您的情况下,zmv 也有一些小优势。特别是,如果发生冲突,zmv 会出错(由于随机因素,除非使用较短的长度,否则极不可能发生)。但由于名字转换比较困难,使用zmv比较别扭,简单的循环更容易写。
这是使用随机名称的完整片段。
setopt extended_glob
typeset -A mapping
mapping=()
for f in *.(foo|<->|baz|bar<->(|-<->).baz); do
base=${f%%(.(foo|<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
if ((!$+mapping[$base])); then
mapping[$base]=$(openssl rand -hex 8)
fi
new_base=$mapping[$base]
mv -i -- $f $new_base$extensions
done
这是使用给定值 的确定性名称的完整片段$secret
。
setopt extended_glob
secret=$(openssl rand -hex 32)
for f in *.(foo|<->|baz|bar<->(|-<->).baz); do
base=${f%%(.(foo|<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
new_base=${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}
mv -i -- $f $new_base$extensions
done
zmv
这是用于确定性情况的单行代码,使用第一个.foo.
标记基数的末尾。该-w
标志有助于分解。
autoload zmv
secret=$(openssl rand -hex 32)
zmv -w '*.foo.*' '${"$(openssl dgst -sha256 -hmac $secret <<<$1)"[-16,-1]}.foo.$2'
在随机情况下使用 zmv 比较棘手,因为我们需要保留从一个转换步骤到下一个转换步骤的信息。我们可以将一些代码打包到命令替换中,zmv … '$(…; if …; then mapping[$base]=…; …)'
因为对 的赋值mapping
将位于命令替换子 shell 内,因此只会在子 shell 内产生影响。但是,我们可以使用条件参数赋值${name=word}
mapping[$base]
,仅在未设置时进行设置。
typeset -A mapping; mapping=()
zmv -w '*.foo.*' '${mapping[${1}]=$(openssl rand -hex 16)}.foo.$2'
将 zmv 与不利用 的分解结合使用.foo
(如上面 (1.) 中更复杂的示例)会产生更复杂的代码。仅出于示例目的,这里有一个针对确定性情况的 zmv 调用,用作base
存储基本名称的临时变量。它用于${name::=word}
在参数扩展期间分配给变量,并${…}[0]
从扩展中抑制该部分([0]
从第 0 个字符获取子字符串,该子字符串不存在,因为 zsh 从 1 开始对数组元素和字符串字符进行编号;类似的东西[2,1]
也可以工作)。
zmv '*.(<->|baz|bar<->.baz)' '${${base::=${f%%(.(<->|baz|bar<->(|-<->).baz))#}}[0]}${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}.${f#$base}'
答案2
你可以这样做:
autoload -Uz zmv # best in ~/.zshrc
typeset -A rand
zmv '(*).foo(.*)' '${rand[$1]=$(openssl rand -hex 8)}$2'
或者'(*)(.foo.*)'
不掉落.foo
。
要首先测试,请将-n
选项(试运行)添加到zmv
.
zmv
是一个作为自动加载功能实现的批量重命名工具。
第一个参数是扩展的 glob 模式,第二个参数是经过单词扩展的字符串,用于确定如何使用$1
, ... 引用模式中$2
相应的 s 来删除文件。(...)
${rand[$1]=$(cmd)}
上面设置了关联数组的成员钥匙.foo.
if的输出最右边的左边cmd
之前未设置,这确保您始终获得给定值的相同值钥匙。