次のような名前のファイルがたくさんあります。
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スクリプトだったのですが、もっとうまくいく別のツールがあれば教えてください。
zsh
の場合:
_autoload zmv # best in ~/.zshrc
typeset -A c=()
zmv -n '(*)_<->.txt(#qnOn)' '$1_$((++c[${(b)1}])).txt-renamed' &&
: zmv '(*)-renamed' '$1'
_
(幸せなら、_-n
_(ドライラン)と_:
_を削除してください(そして、ドライランなしで再度実行する前にc=()
を再初期化することを忘れないでください))。
<->
_:範囲内の10進数と一致する_<1-12>
_に似ていますが、ここでは境界が指定されていないため、1つ以上の10進数のシーケンスと一致します。 _[0-9]##
_と書くこともできます。ここで_##
_はzsh
のERE _+
_と同等です。(#q...)
_は glob qualifiers を指定するためのexplicit構文です。n
:数値で並べ替えOn
:名前で逆順に並べ替えます。したがって、上記のn
を使用すると、一致するファイルのリストが数値で逆順に並べ替えられます。$1
_には_(*)
_でキャプチャされたものが含まれるため、__<digits>.txt
_の前の部分。$((++c[${(b)1}]))
を追加します。ここで、_$c
_は以前に宣言した連想配列です。${(b)1}
は_$1
_であり、グロブ文字はエスケープされています(これがないと、_$1
_に_]
_が含まれている場合、正しく機能しません)。-renamed
_サフィックスを追加します)。あなたのサンプルでは、それは:
_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
を削除してください)。
そして、2番目のフェーズとしてzmv '(*)-renamed' '$1'
を再度実行します。
3番目の例として_[0, 3, 10, 20]
_リストを追加した別のサンプルでは、次のようになります。
_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
_サフィックスで名前が付けられたファイルを上書きしないようにします(ただし、_-i
_はmv
それが起こる前にプロンプトを表示します)。または、_-renamed
_サフィックスを追加する代わりに、名前を変更したファイルをrenamed
ディレクトリに移動することもできます。
これは、ファイルが実際にそれらを描いた方法で名前が付けられていることを前提とする仕事を行うためのbashスニペットです(data<one-digit>_<digits>.txt
)。
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
まず、すべてのファイルの名前を「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は11を9の後にソートすることを示します)_
の前の文字のみで安定したソートを行うと言います。 -k1,1
and-s
は、正しい出力を確実に得るためにどちらも重要です。 -k1,1がない場合、残りの行は重複を解決するために使用されますが、これは望ましくありません。-s
がない場合、重複は任意に順序付けされます。残りは簡単です。
まず、次のようにしてみてください。
mkdir x
for base in $(ls data* | sed 's/_[0-9][0-9]*.txt//' | sort -u ) ;do
ls ${base}_*| tac | for i in data1*; do
read x;cp $i x/$x
done
done
これは特殊な場合にのみ機能します。12 11 111のようなパディングされていない数字やdata1_somethingelse1.txtのようなファイル名で予期しない結果が生じる可能性があります。
これは簡単な解決策であり、あなたの状況に非常にうまく機能すると私は信じています。ここでは、最初にプレフィックスによって取得したファイルタイプの数を確認しています。ここで、ファイルタイププレフィックスは、data1 _、data2 _などを意味します。次に、各プレフィックスタイプについて、使用可能なファイルの総数を取得し、totalFilesForEachPrefix
という配列に格納します。
次に、ステップ1でファイルの名前を一時ファイルに変更します。 otemp
拡張子に移動する理由は、名前の衝突を回避し、既存のファイルを上書きするためです。ここでは、1_1 1_2 1_3 1_4などのファイルがあると想定しています。
次に、ステップ2で.otemp
拡張子。
#!/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