複数のファイル名の一部を小文字にしようとすると、名前の変更で「裸語は許可されません」が返される


12

Ubuntu 16.04のフォルダーに2つのファイルがあります。

a1.dat
b1.DAT

名前を変更b1.DATしたいb1.datので、結果としてフォルダーに次のファイルが含まれます。

a1.dat
b1.dat

私は試しました(失敗しました):

$ rename *.DAT *.dat
Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

そして

$ find . -iname "*.DAT" -exec rename DAT dat '{}' \;
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

これを検索しても意味のある解決策はありません...


mmvについても学びたいと思うかもしれません
PlasmaHH

回答:


14

そのエラーは、Perlが原因のようrenameです。引用を使用する必要がありますが、変更する部分を指定し、検索と置換のスタイルを指定するだけで済みます。構文は次のとおりです。

rename -n 's/\.DAT/\.dat/' *

-nテスト後に削除して、実際にファイルの名前を変更します。

隠しファイルを含めるには、コマンドを実行する前にグロブ設定を変更します。

shopt -s dotglob

ファイルの名前を再帰的に変更したい場合は、

shopt -s globstar
rename -n 's/\.DAT/\.dat/' **

または、現在のディレクトリ内またはその下にで終わらない多くのパスがある場合.DATは、2番目のコマンドでそれらのパスを指定することをお勧めします。

rename -n 's/\.DAT/\.dat/' **/*.DAT

ファイルの末尾がで終わらないさまざまな名前の場合、これはより高速になります.DAT[1]

これらの設定をオフにするにはshopt -u、などを使用できますが、shopt -u globstarデフォルトではオフになっており、新しいシェルを開くとオフになります。

この結果、引数リストが非常に長くなる場合は、たとえば次のように使用できますfind

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} \;

以上

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} +

find ... -execwith を使用する+\;、見つかったファイルから引数リストが作成されるため、使用するよりも高速です。もともと問題があるとおっしゃっていたので、使用することは不可能だと思っていましたがargument list too long、問題を回避するために、リストが必要に応じてコマンドの複数の呼び出しに巧妙に分割されることもわかっています。[2]

renameはすべてのファイル名を同じ方法で処理するため、引数リストが複数の呼び出し間で安全に分割できるため、引数リストがどれだけ長いかは問題ではありません。で使用し-execているコマンドが複数の引数を受け入れない場合、またはその引数を特定の順序にする必要がある場合、またはその他の理由で引数リストを分割すると予期しないことが発生する場合は\;、を使用してコマンドを1回呼び出すことができます。見つかったすべてのファイルに対して(引数リストが他のメソッドに対して長すぎる場合、これには長い時間がかかります!)


この答えを改善するための非常に有用な提案をしてくれたEliah Kaganに感謝します。

[1] グロビング時にファイル名を指定する
[2] find ... -execwith +は、引数リストを分割します


ありがとう。(すべてのサブフォルダー内のすべてのファイルに対して)再帰的に行う方法は?
サモ

@サモは私の編集を参照してください:)
Zanna

機能しない:$ rename 's / \。DAT / \。dat /' ** bash:/ usr / bin / rename:Argument list too long
Samo

1
@Samoは-n、テスト後に名前の変更を削除する必要があります-変更される内容を示すためだけです
Zanna

1
CentosとArchでは、renameはこの動作を示しません。WSLでDebianを使用してここに来ました。この構文は機能しました。
xtian

6

できるよ:

rename -n 's/DAT$/\L$&/' *.DAT

-n実際に名前を変更するにはドロップしてください。

  • globパターンは、現在のディレクトリで*.DAT終わるすべてのファイルに一致します.DAT

  • rename置換、DAT$試合DAT終了時に

  • \L$&一致全体を小文字にします。$&試合全体を指します

あなただけのためにしたい場合b1.DAT

rename -n 's/DAT$/\L$&/' b1.DAT

例:

% rename -n 's/DAT$/\L$&/' *.DAT
rename(b1.DAT, b1.dat)

4

他の 回答では、この質問の2つの主要な側面の1つに対処しています。それは、必要な名前変更操作を正常に実行する方法です。この回答の目的は、renameコマンドのコンテキストでの奇妙な「裸語は許可されていません」エラーメッセージの意味を含め、コマンドが機能しなかった理由を説明することです。

この回答の最初のセクションはrename、Perlとの関係renameと、コード引数である最初のコマンドライン引数をどのように使用するかについてです。2番目のセクションは、シェルが引数リストを構築するために拡張(具体的にはグロビング)を実行する方法についてです。3番目のセクションは、「Bareword not allowed」エラーを発生させるPerlコードで何が起こっているかについてです。最後に、4番目のセクションは、コマンドを入力してからエラーが発生するまでの間に行われるすべてのステップの要約です。

1. renameが奇妙なエラーメッセージを表示したら、検索に「Perl」を追加します。

DebianとUbuntuでは、renameコマンドはファイル名の変更を実行するPerlスクリプトです。このリリースの時点でまだサポートされている14.04 LTSを含む古いリリースでは、これはコマンドを指す(間接的にシンボリックリンクでした。新しいリリースでは、代わりに新しいコマンドを指します。これら2つのPerlのrenameコマンドはほとんど同じように機能します。この回答の残りの部分については、両方を参照するだけにします。prenamefile-renamerename

このrenameコマンドを使用すると、誰かが書いたPerlコードを実行しているだけではありません。また、独自のPerlコードを記述してrename、それを実行するように指示しています。これは、のようなオプション引数以外の、コマンドに渡す最初のコマンドライン引数が実際のPerlコード構成されるためですrename-nrenameコマンドは、それぞれの上で動作させるために、このコードを使用してパス名を使用すると、それに続くコマンドライン引数として渡していること。(パス名引数を渡さない場合は、代わりに1行に1つずつ、標準入力renameからパス名を読み取ります。)

コードはループ内で実行され、ループはパス名ごとに1回繰り返されます。ループの各反復の先頭で、コードが実行される前に、現在処理されているパス名が特殊$_変数に割り当てられます。コードによっての値が別の値に$_変更される場合、そのファイルは新しい名前を持つように名前が変更されます。

Perlの多くの$_オペランドとして使用する他の式が指定されていない場合、変数に対して暗黙的に動作します。たとえば、置換式$str =~ s/foo/bar/は、変数foo保持する文字列内で最初に出現するをに変更するか、が含まれていない場合はそのままにします。あなただけ書いた場合、明示的に使用せずに作業を、それは上で動作します。これは、の略です。$str barfoos/foo/bar/=~$_s/foo/bar/$_ =~ s/foo/bar/

それは合格するのが一般的だ表現をするコードの引数(すなわち、最初のコマンドライン引数)として、しかし、あなたがする必要はありません。ループ内で実行する任意のPerlコードを指定して、の各値を調べ、(条件付きで)変更できます。s///rename$_

これには多くのクールで有用な結果がありますが、それらの大多数はこの質問と回答の範囲をはるかに超えています。これをここで取り上げる主な理由-実際、私がこの回答を投稿することにした主な理由-の最初の引数renameは実際にはPerlコードであるため、奇妙なエラーメッセージが表示されて、検索によってそれに関する情報を見つけるのが困難な場合は、検索文字列に「Perl」を追加して(または「rename」を「Perl」に置き換えることもできます)、答えを見つけることがよくあります。

2. rename *.DAT *.datで、renameコマンドは見たことがない*.DAT

以下のようなコマンドは、rename s/foo/bar/ *.txt一般的に合格しない*.txtと、コマンドライン引数としてrenameプログラム、およびあなたはそれをしたくない、あなたはその名を文字通りあるファイルがない限り*.txt、うまくいけば、あなたはしないでください。

rename解釈しないグロブパターンのような*.txt*.DAT*.datx**y、または*パス名の引数として渡されたし。代わりに、シェルはそれらに対してパス名展開実行します(ファイル名展開とも呼ばれ、グロビングとも呼ばれます)。これは、renameユーティリティが実行される前に発生します。シェルはグロブを潜在的に複数のパス名に展開し、それらをすべて個別のコマンドライン引数としてに渡しますrename。Ubuntuでは、変更していない限り、インタラクティブシェルはBashです。そのため、上記のBashリファレンスマニュアルにリンクしました。

グロブパターンが単一の展開されていないコマンドライン引数としてに渡される可能性がある状況が1つありrenameます。それがどのファイルとも一致しない場合です。この状況では、シェルが異なるとデフォルトの動作も異なりますが、Bashのデフォルトの動作は、単にglobをそのまま渡すことです。ただし、これが必要になることはめったにありません。必要な場合は、引用してパターンが拡張されないようにする必要があります。これは、だけでなく、任意のコマンドに引数を渡す場合にも当てはまります。rename

引用符は、グロビング(ファイル名の展開)だけではありません。引用符で囲まれていないテキストに対して、また引用符で囲まれたテキストに対してシェルによって実行される他の展開があるためです。一般に、スペースを含むシェルによって特別に処理される可能性のある文字を含む引数を渡したいときはいつでも、できればquotesでそれを引用する必要があります" "' '

Perlコードにs/foo/bar/は、シェルによって特別に扱われるものは含まれていませんが、私もそれを引用して-と記述しておいたほうがよいでしょう's/foo/bar/'。(私はまだ引用について話していなかったとして実際には、唯一の理由は、私は、それが一部の読者に混乱するだろうとしていなかった。)それはPerlのコードがあることは非常に一般的ですので、私はこれは良いされているだろうと言う理由がある含まれていますそのような文字、そしてそのコードを変更する場合、引用が必要かどうかを確認するのを忘れたかもしれません。対照的に、シェルでグロブを展開する場合は、引用符で囲まないでください。

3. Perlインタープリターが「裸語は許可されない」とはどういう意味か

質問で示したエラーメッセージはrename *.DAT *.dat、を実行すると、シェル*.DATが1つ以上のファイル名のリストに展開され、それらのファイル名の最初がであったことを示していますb1.DAT。後続のすべての引数(他の展開元*.DATと展開*.dat元の両方)は、その引数の後にあるため、パス名として解釈されます。

実際に実行されたものはのようなものrename b1.DAT ...でありrename、最初の非オプション引数をPerlコードとして扱うため、問題は次のようになりb1.DATます。

Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

シェルでは、文字列を引用して、そうでなければ自動的に他の文字列に変換される意図しないシェル展開から保護します(上記のセクションを参照)。シェルは、汎用言語とは非常に異なる方法で機能する特別な目的のプログラミング言語です(その非常に奇妙な構文とセマンティクスは、それを反映しています)。しかし、Perlは汎用プログラミング言語であり、ほとんどの汎用プログラミング言語と同様に、Perlで引用する主な目的は文字列を保護することではなく、文字列を記述することです。これは実際、ほとんどのプログラミング言語が自然言語にいる方法です。英語では、犬がいるとすると、「あなたの犬」は2語のフレーズですが、あなたの犬は犬です。同様に、Perlでは'$foo'は文字列$fooですが、名前は$fooです。

ただし、他のほぼすべての汎用プログラミング言語とは異なり、Perlは引用符で囲まれていないテキストを文字列を言及しているものと解釈することあります。同じ注文。それが語である場合(または他のシギル、以下を参照)、コードを解釈する他の意味を見つけることができなかった場合にのみ、コードをそのように解釈しようとします。次に、制限を有効にすることで指示しない限り、文字列として受け取ります$

Perlの変数は、通常、変数の幅広いタイプを指定する、sigilと呼ばれる句読文字で始まります。たとえば、$スカラー@意味し、配列%意味し、ハッシュは意味します。(他のものがあります。)Doがない心配あなたが混乱(または退屈な)ことを発見した場合、私は唯一と言って、それを育てていますので、ときに有効な名前は Perlプログラムに表示されますがされていない印章が先行し、その名前裸語と言われる

ベアワードはさまざまな目的に役立ちますが、通常、プログラム(またはプログラムで使用されるモジュール)で定義されている組み込み関数またはユーザー定義のサブルーチンを示します。Perlは何と呼ばれる機能を内蔵していないb1DAT、Perlインタプリタがコードを見ているときにb1.DAT、それが治療しようとするb1と、DATサブルーチンの名前として。そのようなサブルーチンが定義されていないと仮定すると、これは失敗します。次に、制限が有効になっていない場合、制限を文字列として扱います。これは機能しますが、実際にこれを実現するつもりであったかどうかは誰にもわかりません。Perlの.演算子は文字列を連結して、b1.DAT文字列に評価b1DAT。つまり、やのb1.DATようなものを書くための悪い方法です。'b1' . 'DAT'"b1" . "DAT"

これを自分でテストperl -E 'say b1.DAT'するには、短いPerlスクリプトsay b1.DATをPerlインタープリターに渡して実行するコマンドを実行しますb1DAT。(そのコマンドでは、' '引用符が合格するためにシェルを伝えるsay b1.DAT単一のコマンドライン引数として、そうでない場合は、スペースが原因となるsayb1.DATされるように、別の単語として解析し、perl個別の引数としてそれらを受け取ることになる。perlではないとして、引用符自体を見ますシェルはそれらを削除します。)

しかし今、use strict;前にPerlスクリプトで書いみてくださいsay。今それはあなたが得たのと同じ種類のエラーで失敗しますrename

$ perl -E 'use strict; say b1.DAT'
Bareword "b1" not allowed while "strict subs" in use at -e line 1.
Bareword "DAT" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.

これuse strict;は、Perlインタープリターがベアワードをストリングとして処理することを禁止したために起こりました。この特定の機能を禁止するには、subs制限のみを有効にするだけで十分です。このコマンドは、上記と同じエラーを生成します。

perl -E 'use strict "subs"; say b1.DAT'

しかし、通常、Perlプログラマーはと書くだけでuse strict;subs制限と他の2つが可能になります。use strict;一般的に推奨される方法です。したがって、renameコマンドはコードに対してこれを行いますそのため、そのエラーメッセージが表示されます。

4.要約すると、これが起こったことです。

  1. シェルがb1.DAT最初のコマンドライン引数として渡されました。これrenameは、各パス名引数のループで実行されるPerlコードとして扱われました。
  2. これは、オペレーターを意味しb1DAT接続されていると解釈されました.
  3. b1また、DAT接頭辞が付いていないため、それらは裸語として扱われました。
  4. これらの2つの裸語は、組み込み関数またはユーザー定義のサブルーチンの名前と見なされていましたが、そのような名前はありませんでした。
  5. 「厳密なサブ」が有効になっていない場合、それらは文字列式'b1'と同様に扱われ、'DAT'連結されます。これは、この機能が役に立たないことが多いことを示す、意図したものとはかけ離れています。
  6. しかし、「厳格な潜水艦は」理由は、有効になっていたrenameすべての可能な制限をvarsrefs、およびsubs)。したがって、代わりにエラーが発生しました。
  7. renameこのエラーのために終了しました。この種のエラーは早期に発生したため、パスしなかった場合でも、ファイル名の変更は行われませんでした-n。これは、ユーザーを意図しないファイル名の変更から保護し、場合によっては実際のデータ損失から保護することもできます。

Zanna感謝します。Zannaは、この回答のより簡潔なドラフトでいくつかの重要な欠点に対処するの助けてくれました。彼女がいなければ、この答えはあまり意味がなく、投稿されないかもしれません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.