POSIX 移植性とともに、すべてのタイプの文字を使用して多数のファイル名を変更します。

POSIX 移植性とともに、すべてのタイプの文字を使用して多数のファイル名を変更します。

時にはディレクトリ内のすべてのファイルの名前を変更する必要があります(名前変更規則は後で説明します)。ここで、ファイル名は常に「filenamename.extension」形式です(拡張子は常に存在し、変更されます)。名前には、[:graph:]クラスのスペースと文字を含めることができます。最初の質問は、* NIXシステム(特にLinux、BSD、およびAIXなどの他のシステム)間で完全に移植可能でなければならないことです。 2番目の質問は[:graph:]クラスについてです。ファイル名は次のとおりです。

cat.txt
dog_and_cat.txt
Where is the cat?.png
my.cat.is.cute.txt.js.html
;;; ;;; ;;;.......321
áéúő _[a lot of whitespaces]_ óü^^^^^ö.jpg

これを処理してforループに入れることが難しいことが簡単にわかります。例えば、

for i in *; do something; done

特に、さまざまなオペレーティングシステムでは、空白と奇妙な文字が常に好まれるわけではありません。

名前変更規則は、すべてのファイルの名前をmd5sumなどの特定の$FOOBAR.$EXTENSION形式のハッシュに変更することです。$FOOBARforループ内には次のような行があります。

mv $FILE $(md5sum $FILE | sed 's/\ \ .\+//');

ファイルを独自の md5sum に移動しますが、拡張子は消えます。拡張機能を維持したい。ほぼ常にフォーマットです.[a-zA-Z0-9]{1,3}。時には.tar.gz同様の拡張を維持する必要があるかもしれません(もちろん変数に追加することができますMYEXTENSIONS='tar.gz tar.bz2 foo.bar')。

私の直感では、この問題はよくパラメータ化された基本的なUNIX /シェルコマンドで解決できると言っていますが、今は非常に難しいです。答えからたくさん学ぶことができると確信しています。私が魔法の言葉を言ったことを知っています。移植性ただし、言語を指定する必要がある場合、好ましい解決策はbashです。

答え1

実際にaで始まるファイル名がワイルドカード一致から除外されるfor i in *; do something; doneことを除いて、すべてのファイル名は正しく処理されます。.すべてのファイル(.および除外..)を移植可能に一致させるには、一致しない* .[!.]* ..?*パターンがそのまま残り、存在しないファイルを一致させてスキップします。

$i問題が発生した場合は、後で引用符が正しくないためです。変数置換とコマンド置換は常に二重引用符で囲みます"$foo""$(cmd)"フィールド分割とワイルドカードの発生を計画しない限り。

ファイル名を外部コマンドに渡す必要がある場合(ここでは必要ありません)、必ずしも文字通りecho "$foo"印刷$fooされるわけではありません。一部のシェルはバックスラッシュ拡張を実行し、$fooで始まるいくつかの値は-オプションとして扱われます。文字列を正確に印刷する安全でPOSIX互換の方法は次のとおりです。

printf '%s' "$foo"

またはprintf '%s\n' "$foo"最後に改行文字を追加してください。注目すべきもう1つの点は、コマンド置換が末尾の改行を削除することです。改行を維持する必要がある場合は、可能な1つの方法は、改行以外の文字をデータに追加し、変換時にその文字が保持されることを確認してから、最後にその文字を切り捨てることです。たとえば、

mangled_file_name="$(printf '%sa' "$file_name" | tr -sc '[:alnum:]-+_.' '[_*]')"
mangled_file_name="${mangled_file_name%a}"

ファイルのmd5sumを抽出するには、出力にファイル名を含めないでくださいmd5sum。これにより削除が困難になります。データをmd5sum標準入力に渡します。

このmd5sumコマンドは POSIX にはありません。一部のUNIXバリアントにはこの機能があるか、md5まったくありません。cksumPOSIXですが、競合が発生しやすいです。

バラよりファイル名の拡張子を取得します。ファイル拡張子を取得する方法。

みんなでまとめてみましょう(テストされていません)。ここにあるすべての機能はPOSIXシェルで動作しますが、いくつかのbash機能を提供しますが、あまり多くはありません。

for old_name in * .[!.]* ..?*; do
  if ! [ -e "$old_name" ]; then continue; fi
  hash=$(md5sum <"$old_name")
  case "$old_name" in
    *.*.gz|*.*.bz2)                   # double extension
      ext=".${old_name##*.}"
      tmp="${old_name%.*}"
      ext=".${old_name##*.}$ext";;
    ?*.*) ext=".${old_name##*.}";;    # simple extension
    *) ext=;;                         # no extension
  esac
  mv -- "$old_name" "$hash$ext"
done

指定された名前の宛先ファイルがすでに存在する場合は考慮されません。特に名前が採用したルールに似ていますが、チェックサム部分がファイルの内容と一致しませんが、同じ拡張子を持つ他のファイルの内容が相対的な事前編成順序に依存する既存のファイルがある場合はどうなりますか?ファイル名。

答え2

かなり複雑な質問なので、いくつかありますが、お知らせします。ガイドライン:

  • 二重引用符ファイル名変数はどこにでもあります。これにより、単語の分割によるほとんどすべての空白の問題を回避できます。
  • 内部変数は$()外部構造と同様に参照する必要があります。追加のエスケープは必要ありません。
  • これ$()``末尾の改行文字でストリップを構成します。したがって、別の文字を追加してから構成から削除する必要があります$()

    varx="$([command which might print a value ending in \n]; echo x)"
    var="${varx%x}"
    
  • --コマンドに必要ファイル名からパラメータを区切ります。、ファイル名はで始まる可能性があるため、--パラメータとして扱われます。
    • findこの構文はサポートされていないため、readlink定義に従ってスラッシュで始まる絶対パスを取得するか、指定されたパスがすでに絶対パスまたはでfind始まるかどうかを確認します./
  • <(転送プロセスが終了したときにパイプが損傷するのを防ぐために、パイプの代わりにプロセス置換を使用してください。
  • 貪欲なコマンド(すべてのデータを飲み込んcatだり飲み込んだりするなど)を避けるために、データ転送に標準入力の代わりに3から9のファイル記述子を使用してください。ssh
  • まず、テスト!私は通常、上記の内容をテストするためにこのファイル名を使用します。$'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n'

関連情報