我有一個文件集列表,其中有很多擴展名,但名稱唯一。
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
之前未設置,這確保您始終獲得給定的相同值鑰匙。