ファイル名の削除と名前変更

ファイル名の削除と名前変更

ディレクトリから現在のファイル名を削除して英数字のファイル名に変更するには、どの名前変更コマンドを使用できますか?

例えばgg0001、、、gg0002ちょっと待って?私はこのコマンドがすべての種類の拡張子を持つファイルを処理できるようにしたいです。その拡張を維持したい。

rename私はPerlを使用することを好みます。

このコマンドを試しましたが、ファイル名の前に「gg」が追加されますが、元のファイル名を変更したいと思います。

rename 's/^/gg_/' *

答え1

実際にはコマンドは必要なく、renameシェルで直接実行できます。

c=0; for i in *; do let c++; mv "$i" "gg_$(printf "%03d" $c)${i#"${i%.*}"}"; done

printf "%03d" $c変数は印刷され、必要$cに応じて前にゼロが3つ追加されます。let c++カウンター増加() $c。拡張子を抽出します${i#"${i%.*}"}。関連コンテンツをもっと見るここ

私はこれを使用しませんrename。計算できないと思います。有効なPerlコンストラクタにs/.*/$c++/e失敗し、計算を実行する他の方法はありません。これはまだループで実行し、他のものがカウンタを増やす必要があることを意味します。それは次のとおりです。

c=0;for i in *; do let c++; rename -n 's/.*\.(.*)/'$c'.$1/' "$i"; done

しかし、より簡単な方法を使用するよりも利点はありませんmv

答え2

ファイル名に一重引用符がない場合は、次のことが'できます。

i=0; set --
for f in *
do set -- "$@" "$f" gg "$((i+=1))" "${f#"${f%.*}"}"
done
printf "mv '%s' '%s%03d%s'\n" "$@" | sh

これにより、ファイル名にaが含まれている場合は、.そのファイルとその後のすべての内容が保存されます。それ以外の場合、フィールドは空でprintf何も印刷されません。

私は思っていてもはいファイル名に一重引用符を使用してください。

i=0; set --
for f in *
do set -- "$@" "$((i+=1))" gg "$i" "$i#\"\${$i%.*}\""
done
printf 'mv "${%d}" "%s%03d${%s}"\n' "$@" | sh -s -- *

...おそらく全体的に安全です。

ところで考えてみると、どちらもとても大変だったと思い始めました。 2つのリストのいずれかをパイプする唯一の理由は、ゼロパディングをチェックしないためです。私はサブシェルにリストを作成し、必要に応じてあるwrite()サブシェルから別のサブシェルにストリーミングすることを好みます。

しかし、ゼロパディングはそれほど難しくありません。私はいくつかの調査を行った結果、次のような小さな数学的な説明を思いついた。

$((z/=(m*=((i+=1)<$m?1:10))/$m))

繰り返しループに配置すると、各項目を一度増やし、各要素10に対して指数フォールバックを使用して乗数を前進させ、$i同じ項目ごとに什分の一を課します。アイデアは、ループを開始する前に可能な限り高い増分を維持する値を取得することです。これはとても簡単です。$m$z$z$i

set -- *; max=$# z=1
until [ "${#z}" -gt "${#max}" ]; do : "$((z*=10))"; done

これは完全に自動化された方法です。可能な限り最大数まで自動的に入力されます。しかし、正直に言うと、$z1で始まり、その後にゼロだけが続く数字です。

set -- *; z=1000

...何千ものフィールドがいっぱいです。とにかくテスト中に使用したいくつかのデモは次のとおりです。

time dash <<\CMD
    str=100000 z=1 m=1 i=0 
    until [ "${#z}" -gt "${#str}" ]; do : "$((z*=10))"; done
    while : "$((z/=(m*=((i+=1)<$m?1:10))/$m))" &&
    case "$i" in (*[2-8]*|*9*[10]*|*1*[19]*) continue;;
    ([019]*) set -- "$@" "z: $z m: $m i: $i" "${z#?}$i";;esac
    do [ "$i" = "$str" ] && printf %s\\n "$@" && break; done
CMD

多数の項目を処理でき続けるかどうかを確認したいが、100,000件の結果を読みたくないため、ほとんどはフィルタリングされた出力です。

出力

z: 100000 m: 10 i: 1
000001
z: 100000 m: 10 i: 9
000009
z: 10000 m: 100 i: 10
000010
z: 10000 m: 100 i: 99
000099
z: 1000 m: 1000 i: 100
000100
z: 1000 m: 1000 i: 999
000999
z: 100 m: 10000 i: 1000
001000
z: 100 m: 10000 i: 9999
009999
z: 10 m: 100000 i: 10000
010000
z: 10 m: 100000 i: 99999
099999
z: 1 m: 1000000 i: 100000
100000
dash <<<''  0.32s user 0.00s system 99% cpu 0.320 total

$m先行ゼロ除去を開始する最初のステップに初期化する必要があります。m=1上記で行った作業です。$z上記のように、最高のサイズが保存されます。$iループ数を数えるので、おそらくi=0安全な賭けでしょう。とにかく残りは起こります。アイデアは、リードをはがしてどこにでも取り付ける1ことです。$z

set -- $(seq 1000); m=1 i=0 z=10000
while [ "$#" -gt 0 ] && : "$((z/=(m*=((i+=1)<$m?1:10))/$m))"
do printf 'mv "%s" "lead_string_%s"\n' "$1" "${z#1}$i${1#"${1%.*}"}"
shift; done

私はprintfそれがどのように見えるかを示すためにそれを使用しています。もちろん、ポイントはデータの印刷を避け、代わりに準備された区切り引数で現在のシェルに渡すことです。とにかく次のようになります。

mv "1" "lead_string_0001"
mv "2" "lead_string_0002"
mv "3" "lead_string_0003"
...
mv "9" "lead_string_0009"
mv "10" "lead_string_0010"
mv "11" "lead_string_0011"
...
mv "15" "lead_string_0015"
...
mv "96" "lead_string_0096"
...
mv "99" "lead_string_0099"
mv "100" "lead_string_0100"
mv "101" "lead_string_0101"
mv "102" "lead_string_0102"
...
mv "998" "lead_string_0998"
mv "999" "lead_string_0999"
mv "1000" "lead_string_1000"

foranを使用するかループを使用するかは関係ありませんwhile。したがって、ファイルの場合は次のことができます。

m=1 i=0 z=10000
for f in *; do : "$((z/=(m*=((i+=1)<$m?1:10))/$m))"
mv -- "$f" "lead_string_${z#1}$i${f#"${f%.*}"}"
done

答え3

これは私にとって効果的です。 (しかし、renameここで使用されているコマンドはですperl rename。)

ls -1 -c | xargs rename -n 's/.*/our $i; sprintf("gg%04d", $i++)/e'

ただし、上記は、名前にスペースを含むファイルが存在しないと想定しています。さもなければ壊れるでしょう。また、ls出力を解析することは実際には良い考えではありません。

ただし、ファイル名に空白があると壊れるので上記の問題を解決してみましょう。

find -print0 | xargs -0 rename 's/.*/our $i; sprintf("gg%04d", $i++)/e'

上記の構文は機能しますが、まだ問題があります。問題は、サブディレクトリがあるときにサブディレクトリの名前も変更されることです。これがまさに私たちが望むものです。

この問題を解決するために、@terdonが彼のコメントで提案したようにコマンドを書き換えることができます。最後に修正されたコマンドは次のとおりです。

find -maxdepth 1 -type f -print0 | xargs -0 rename 's/.*/our $i; sprintf("gg%04d", $i++)/e'

テスト

私のフォルダには次のファイルがあります。私の名前を変更しないでください名前を変更したくないサブディレクトリです。

ls
Don't rename me  file1  file with spaces  hello

次に、コマンドを実行して次の出力を取得します。

find -maxdepth 1 -type f -print0 | xargs -0 rename -n 's/.*/our $i; sprintf("gg%04d", $i++)/e'
./file1 renamed as gg0000
./hello renamed as gg0001
./file with spaces renamed as gg0002

-n最終出力に満足したら、フラグを削除できます。

引用する

https://stackoverflow.com/a/23207377/1742825

答え4

繰り返しごとに変数を増やすだけです。renamesetのためにuse strictグローバル変数を使用する必要があります$::i(またはを使用するか、またはをno strict使用して変数を宣言する必要がありますour)。

rename 'our $i; s/.*/gg_$i/; ++$i' *

数字の前にゼロを埋めるには、ファイル数を数えます。ファイルのリストはにあります@ARGV

rename 'our $i; my $width = length(scalar(@ARGV)); s/.*/sprintf("gg_%0${width}d", $i++)/e' *

最初の反復でのみコードを実行するようにカウンタ変数が定義されているかどうかをテストできます。

rename 'our ($i, $width); if (!defined $width) {$width = length(scalar(@ARGV)); $i = 0;} s/.*/sprintf("gg_%0${width}d", $i++)/e' *

上記のすべての場合で、ディレクトリ部分を含むファイル名を渡す場合(つまり、/ここでは発生しない - を含むファイル名を渡す場合*s/.*/…/に変更しますs/[^/]*\z/…/

関連情報