web-dev-qa-db-ja.com

番号順を逆にしてファイル名を変更する

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

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スクリプトだったのですが、もっとうまくいく別のツールがあれば教えてください。

4
AJwr

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_に_]_が含まれている場合、正しく機能しません)。
  • プロセスでのファイルの上書きを避けるために、2段階でそれを行います(2番目の段階で削除される_-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ディレクトリに移動することもできます。

2

これは、ファイルが実際にそれらを描いた方法で名前が付けられていることを前提とする仕事を行うための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
1
iruvar

まず、すべてのファイルの名前を「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の後にソートすることを示します)
  • tacは入力全体を反転します(ファイル全体。我慢してください!)
  • ソートは、最初の_の前の文字のみで安定したソートを行うと言います。 -k1,1and-sは、正しい出力を確実に得るためにどちらも重要です。 -k1,1がない場合、残りの行は重複を解決するために使用されますが、これは望ましくありません。-sがない場合、重複は任意に順序付けされます。

残りは簡単です。

0
sitaram

まず、次のようにしてみてください。

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のようなファイル名で予期しない結果が生じる可能性があります。

0
n3ko

これは簡単な解決策であり、あなたの状況に非常にうまく機能すると私は信じています。ここでは、最初にプレフィックスによって取得したファイルタイプの数を確認しています。ここで、ファイルタイププレフィックスは、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
0
Rakib Fiha