web-dev-qa-db-ja.com

ファイル名を大文字に変更しますが、ファイル拡張子には影響しません

私は顧客のディレクトリでこれを使用して、各Wordの最初の文字がリクエストに従って大文字になっているファイルの名前を変更しています:

rename 's/\b(\w)/\u$1/g' *

それはうまくいきますが、拡張子の最初の文字も大文字にするので、これを使用して修正します:

rename 's/\.Txt$/.txt/' *.Txt

フォルダー内のほとんどのファイルが同じ拡張子(通常はtrue)であれば問題なく動作しますが、それらが非常に混在している場合は苦痛です。

この問題を回避するために、次のような小さなスクリプトを作成しました(笑ってはいけません!)。

#!/bin/bash
rename 's/\.Txt$/.txt/' *.Txt
rename 's/\.Doc$/.doc/' *.Doc
rename 's/\.Docx$/.docx/' *.Docx
...
rename 's/\.Xlsx$/.xlsx/' *.Xlsx

「* .Txt * .txtの名前を変更できません:そのようなファイルまたはディレクトリはありません」エラーを無視し、不足している拡張子を見つけた場合は、その行をスクリプトに追加します。

これは重要ではないと思いますが、ファイルはWindowsサーバーのファイル共有にありますが、Ubuntu 16.04LTSを使用してアクセスしています。問題があれば、まずローカルドライブにコピーし、Ubuntuでコマンドを実行し、必要に応じてファイルを元に戻します。

最初の名前変更コマンドを修正して拡張子を無視し、小文字のままにする方法はありますか?上位レベルのディレクトリで新しいコマンドを実行し、すべてのサブディレクトリを再帰的に実行できますか?

7
Alan

これを達成する1つの方法は、 Negative Lookbehind を使用して、ワード境界-リテラルピリオドが前にない場合のワード文字シーケンスのみと一致させることです。与えられた

$ ls
alan's file.txt  bar.txt  foo

それから

$ rename -n 's/(?<!\.)\b\w*/\u$&/g' *
rename(alan's file.txt, Alan'S File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)

複数形のsも大文字にするのを避けたいと仮定すると、それを

$ rename -n 's/(?<![.'\''])\b\w*/\u$&/g' *
rename(alan's file.txt, Alan's File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)

あるいは

$ rename -n 's/(?<![[:punct:]])\b\w*/\u$&/g' *
rename(alan's file.txt, Alan's File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)
7
steeldriver

これにより、最終的な拡張子を除くすべてのWordの最初の文字が大文字になります。

rename -n 's/\b(.+?)\b/\u$1/g; s/(.+)\.(.)/$1\.\l$2/' *

私はこれらのファイルでテストしました:

$ ls -1
'a file with a '$'\n''new line.foo'
'a file with spaces.txt'
justonelongfilename.ext
no-extensions-here
this.has.many.extensions.pdf

そして、それらの名前を次のように変更します。

$ rename -n 's/\b(.+?)\b/\u$1/g; s/(.+)\.(.)/$1\.\l$2/' *
a file with a 
new line.foo -> A File With A 
New Line.foo
a file with spaces.txt -> A File With Spaces.txt
justonelongfilename.ext -> Justonelongfilename.ext
no-extensions-here -> No-Extensions-Here
this.has.many.extensions.pdf -> This.Has.Many.Extensions.pdf

秘Theは、最初にすべての最初の文字を大文字にし、拡張子を無視してから、戻って拡張子を小文字にすることです。

  • s/\b(.+?)\b/\u$1/g;.+?は貪欲でないパターンです。つまり、最短一致を見つけます。単語の境界(\b)によって固定されているため、これはすべての単語を検出します(すべて最後のgのため)。次に、これらは大文字の(最初の文字が大文字の)バージョン(\l$1)に置き換えられます。

  • s/(.+)\.(.)/$1\.\l$2/.+は貪欲なので、可能な限り最長の一致を見つけます。これは、最後の.までの最長の文字列を意味し、その後に拡張子(存在する場合)が続きます。一致部分を、拡張子($1)、.、および小文字の最初の文字(\l$2)の前のすべてと置き換えます。


再帰も簡単です。シェルがbashの場合(わからない場合はおそらくそうです)、globstarオプションを使用して、**を0個以上のサブディレクトリに一致させることができます。

shopt -s globstar

次に、次のように名前変更コマンドを実行します(これは、現在のディレクトリ内のすべてのファイルでも機能します)。

rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' **

拡張子を持つファイルまたはディレクトリのみに制限するには、**/*.*の代わりに**を使用します。

または、findを使用します。

find /path/to/dir -exec rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' {} +

そして、拡張子を持つファイルとディレクトリに制限するには:

find /path/to/dir -name '*.*' -exec rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' {} +

これらのソリューションはすべて、ファイルだけでなくディレクトリも元気に名前を変更しますに注意してください。そうしたくない場合は、再帰するように指示する場所に注意するか、*.txtなどのより具体的なパターンを指定してください。


すべての例で、-nを削除して実際に何かをさせる。-nrename単に何をするかを印刷し、実際には何もしません。テストに役立ちます。

5
terdon

最も賢明な方法は、拡張子を含むすべての「単語」(Word境界\bを使用し、$1を介して任意の最初の文字を参照する)に取り組むことですが、拡張子自体を小文字にします。

$ prename -nv 's/\b(.)/\u$1/g;s/^(.*)\.(.*)/$1.\l$2/' *                                                                                                                
another filename trispaced.txt renamed as Another Filename Trispaced.txt
filename_with_underschore.txt renamed as Filename_with_underschore.txt
one filename.txt renamed as One Filename.txt

これはアンダースコアを含むファイル名では機能しないことに注意してください。つまり、「Word」境界は空白(タブ、スペース、改行-いずれにしてもファイル名に含まれないこと)で考慮されます。

prenameへの移植性のためのkshの使用に注意してください。ここで、renameは実際にはスタンドアロンのPerl実行可能ファイルではなく、組み込みコマンドです。

4

Gフラグなしでrename 's/\b(\w)/\u$1/' *を使用するだけです。
gフラグは「global = do it multiple times」を意味します。ファイル名に1回目を作成し、2回目に大文字にしたくないですか?

1
V-Mark