数値の順序を逆にしてファイル名を変更する

数値の順序を逆にしてファイル名を変更する

次のような名前のファイルがたくさんあります。

data1_1.txt
data1_2.txt
data1_3.txt
data2_1.txt
data2_2.txt
...

ただし、それらは逆順にダウンロードされ、名前が付けられます。結果が次のようにまとめて名前を変更するにはどうすればよいですか?

data1_3.txt
data1_2.txt
data1_1.txt
data2_2.txt
data2_1.txt
...

私の最初のアイデアは単にbash / zshスクリプトですが、うまく機能する他のツールがある場合は教えてください。

答え1

そしてzsh

autoload zmv # best in ~/.zshrc

typeset -A c=()
zmv -n '(*)_<->.txt(#qnOn)' '$1_$((++c[${(b)1}])).txt-renamed' &&
  : zmv '(*)-renamed' '$1'

(必要に応じ-nて(ドライラン)を取り外し、:(ドライランなしで再実行する前に再初期化することを忘れないでくださいc=())。

  • <-><1-12>:10進範囲一致と似ていますが、ここでは境界が指定されていないため、1つ以上の10進数シーケンスが一致します。[0-9]##where ##is がzshERE と同じであると書くこともできます+
  • (#q...)~である明らかに構文の指定グローバル予選
  • n:数値順に並べ替え
  • On:名前に基づいて逆順にソートします。したがって、n上記の方法を使用すると、一致するファイルのリストを番号順に並べ替えることができます。
  • 交換するには、前の部分$1でキャプチャした内容を含めます。(*)_<digits>.txt
  • を追加します$((++c[${(b)1}]))。ここで、は$c以前に宣言された連想配列です。
  • ${(b)1}エスケープ付きのグローバル文字(エスケープがないと$1含まれていると正しく機能しません)。$1]
  • -renamedプロセスがファイルを上書きするのを防ぐために、2つのステップ(2番目のステップで削除されたサフィックスを追加)でこれを行います。

サンプルでは、​​以下を提供します。

mv -- data2_2.txt data2_1.txt-renamed
mv -- data2_1.txt data2_2.txt-renamed
mv -- data1_3.txt data1_1.txt-renamed
mv -- data1_2.txt data1_2.txt-renamed
mv -- data1_1.txt data1_3.txt-renamed

mv -- data1_1.txt-renamed data1_1.txt
mv -- data1_2.txt-renamed data1_2.txt
mv -- data1_3.txt-renamed data1_3.txt
mv -- data2_1.txt-renamed data2_1.txt
mv -- data2_2.txt-renamed data2_2.txt

技術的にはそうではありません。逆順でまたは、例のように数字が1ずつ増え、1から始まる場合にのみ実行します。[1, 2, 3]、、[4, 5, 6]すべて[0, 10, 20]に変わります[3, 2, 1]

そのリストを裏返すには少し複雑です。次のように見えます。

all_files=(*_<->.txt(n))
prefixes=(${all_files%_*})

for prefix (${(u)prefixes}) {
  files=(${(M)all_files:#${prefix}_<->.txt})
  new_files=(${(Oa)^files}-renamed)
  for old new (${files:^new_files})
    echo mv -i -- $old $new-renamed
}

(幸せなら削除echo)。

zmv '(*)-renamed' '$1'2番目の手順でやり直してください。

[0, 3, 10, 20]3番目の例として、追加のリストがある別の例では次のようになります。

mv -i -- data1_1.txt data1_3.txt-renamed
mv -i -- data1_2.txt data1_2.txt-renamed
mv -i -- data1_3.txt data1_1.txt-renamed
mv -i -- data2_1.txt data2_2.txt-renamed
mv -i -- data2_2.txt data2_1.txt-renamed
mv -i -- data3_0.txt data3_20.txt-renamed
mv -i -- data3_3.txt data3_10.txt-renamed
mv -i -- data3_10.txt data3_3.txt-renamed
mv -i -- data3_20.txt data3_0.txt-renamed

これらのソリューションは、ファイル名に含めることができる文字(または文字以外の文字)については想定していません_<digits>.txt。ベースのアプローチで起動しない限り、ファイル名を変更しません。後者のアプローチとは異なり、既存のサフィックスで名前付きファイルを上書きzmvするのを防ぎます(これが発生する前にメッセージが表示され-renamedますが)。または、サフィックスを追加する代わりに、名前が変更されたファイルをディレクトリに移動することもできます。-imv-renamedrenamed

答え2

以下は、ファイル名が実際に説明する方法(data<one-digit>_<digits>.txt)であると仮定するbashスニペットです。

shopt -s extglob

#gather files into array
files=( data[[:digit:]]_+([[:digit:]]).txt )

#zip original files with their target file names and feed to mv
paste <(printf '%s\n' "${files[@]}" | sort -k1.5,1n -k2n -t'_') \
    <(printf '%s.ren\n' "${files[@]}" | sort -k1.5,1n -k2nr -t'_') | 
    xargs -n 2 mv --

#strip the temporary .ren suffix
for f in data*.ren; do mv -- "$f" "${f%.ren}"; done

答え3

まず、「old-」というプレフィックスが付いたすべてのファイルの名前を変更します。

for i in *
do
    mv "$i" "old-$i"
done

その後、このコマンドを実行して出力を観察して、正しく表示されていることを確認します。

ls -v | tac | sort -s -t _ -k1,1 | sed -e 's/^old-//' | paste <(ls -v) - | sed -e 's/^/mv /'

その場合は、出力をshにパイプします。

これが起こったことです。

  • ls -vソートされた順序で生成します。たとえば、-vは9の後に11をソートすることを意味します。
  • tacは完全な入力を逆にします(完全なファイルです。忍耐を持ってください!)
  • 並べ替えは、最初の文字より前の文字のみが安定して並べ替えられることを意味します_。これ-k1,1 そして -s正しい出力を得るためには、両方とも重要です。 -k1,1 がなければ、行の残りの部分は私たちが望んでいない重複を解決するために使用され、重複がなければ順序は-s任意です。

残りは簡単です。

答え4

これは簡単な解決策であり、あなたの状況に完全に機能すると信じています。ここで行うことは、まずプレフィックスで取得したファイル形式の数を確認することです。ここで、ファイルタイプのプレフィックスは data1_ を表します。、データ2_など。次に、プレフィックスタイプごとに利用可能なファイルの総数を取得し、名前付き配列に保存しますtotalFilesForEachPrefix

次に、手順1でファイル名を一時ファイルに変更します。拡張子に移動する理由otempは、名前の競合を防ぎ、既存のファイルを上書きすることです。ここでは、1_1 1_2 1_3 1_4などのファイルがあるとします。

.otemp次に、手順2で拡張機能をアンインストールします。

#!/usr/bin/env bash
totalFileTypeByPrefix=$( for file in data*_1.txt; do echo $file; done | wc -l )
totalFilesForEachPrefix=()
for (( prefix = 1; prefix <= totalFileTypeByPrefix; prefix++ )); do
  totalFilesForEachPrefix+=( $( for file in data${prefix}_*.txt; do echo $file; done | wc -l) )
done
### Step 1
prefix=1; type=0;
while (( prefix <= totalFileTypeByPrefix )); do
  suffix=${totalFilesForEachPrefix[$type]}
  for file in data${prefix}_*.txt; do
    mv $file data${prefix}_${suffix}.txt.otemp; echo "$file renamed temporary --> data${prefix}_${suffix}.txt.otemp"
    suffix=$((suffix -1))
  done
  type=$((type+1))
  prefix=$((prefix+1))
done
### Step 2
echo "....Finally changing the temporary files"....
for tempfile in *.otemp; do
  file=${tempfile::-6};
  mv $tempfile $file; echo "$tempfile renamed final --> $file";
done

以下は、何が起こっているのかについての出力と説明です。

data1_1.txt renamed temporary --> data1_3.txt.otemp
data1_2.txt renamed temporary --> data1_2.txt.otemp
data1_3.txt renamed temporary --> data1_1.txt.otemp
data2_1.txt renamed temporary --> data2_2.txt.otemp
data2_2.txt renamed temporary --> data2_1.txt.otemp
....Finally changing the temporary files....
data1_1.txt.otemp renamed final --> data1_1.txt
data1_2.txt.otemp renamed final --> data1_2.txt
data1_3.txt.otemp renamed final --> data1_3.txt
data2_1.txt.otemp renamed final --> data2_1.txt
data2_2.txt.otemp renamed final --> data2_2.txt

関連情報