在 shell 中重新命名具有多個(包括複合)副檔名的檔案集

在 shell 中重新命名具有多個(包括複合)副檔名的檔案集

我有一個文件集列表,其中有很多擴展名,但名稱唯一。

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

也會好的。還有一些問題:

  1. 如何從檔案開頭到 .foo 定位?
  2. 如何循環創建的變量,例如result=$(openssl rand -hex 8)以便在重命名中使用它,並且僅當一組完成時,才能再次分配它以循環它直到下一組完成等?

謝謝!

答案1

這個問題有幾個部分:

  1. 將每個檔案名稱分解為基本部分和副檔名。
  2. 對每個名稱的基本部分套用一致的轉換。
  3. 根據所選的基礎元件轉換重新命名文件,保留副檔名。

1. 分解檔名

從您的範例名稱中尚不完全清楚您認為檔案名稱的基本部分是什麼。分隔符號顯然是一個點,但在像這樣的範例中yet.a.different.file.name.foo.bar1-2.baz,哪個點?您提到使用 的嘗試*.(<->|baz|bar<->.baz),它不會將foobar1-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之前未設置,這確保您始終獲得給定的相同值鑰匙

相關內容