web-dev-qa-db-ja.com

WindowsまたはMacでの競合を避けるためにファイル名を変更する方法は?

たとえば、他のファイルシステムと競合する文字が含まれないように、ファイル名をバッチで変更するにはどうすればよいですか。

Screenshot 2015-09-07-25:10:10

このファイル名ではコロンが問題であることに注意してください。これらは、WindowsまたはMacで消化されません。

これらのファイルの名前は

Screenshot 2015-09-07-25--10--10

Ubuntuから別のOSに大量のファイルを移動する必要があります。 Rsyncを使用してNTFSドライブにコピーしましたが、いくつかのファイルが失われました。また、ext4ドライブにコピーしました。

次のリストは予約文字です。

< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)

もう1つの問題は、Windowsではファイル名に関して大文字と小文字が区別されないことです(ほとんどのOS Xシステムも同様です)。

5
don.joey

次のようなことができます:

rename 's/[<>:"\\|?*]/_/g' /path/to/file

これにより、これらのすべての文字が_に置き換えられます。 /を置き換える必要はないことに注意してください。これは両方のファイルシステムのファイル名には無効な文字ですが、Unixパス区切り文字として使用されます。次を使用して、ディレクトリとそのすべてのコンテンツに拡張します。

find /path/to/directory -depth -exec rename 's/[<>:"\\|?*]/_/g' {} +

/(パターンの終わりを示す)と\の両方がエスケープされることに注意してください。一意性を保持するには、ランダムなプレフィックスを追加できます。

$ rename -n 's/[<>:"\/\\|?*]/_/g && s/^/int(Rand(10000))/e' a\\b
a\b renamed as 8714a_b

より完全なソリューションは、少なくとも次のとおりです。

  1. すべての文字を同じ大文字に変換します
  2. 正気のカウントシステムを使用する

つまり、foo.mp3foo.mp3.1ではなく、foo.1.mp3になります。Windowsは拡張機能に依存しているためです。

それを念頭に置いて、私は次のスクリプトを書きました。元のファイルを変更するのではなく、名前を変更したファイルをコピーできるプレフィックスパスを使用して、非破壊的にしようとしました。

#! /bin/bash

windows_chars='<>:"\|?*'
prefix="windows/"

# Find number of files/directories which has this name as a prefix
find_num_files ()
(
    if [[ -e $prefix$1$2 ]]
    then
        shopt -s nullglob
        files=( "$prefix$1-"*"$2" )
        echo ${#files[@]}
    fi
)

# From http://www.Shell-fu.org/lister.php?id=542
# Joins strings with a separator. Separator not present for
# Edge case of single string.
str_join ()
(
    IFS=${1:?"Missing separator"}
    shift
    printf "%s" "$*"
)

for i
do
    # convert to lower case, then replace special chars with _
    new_name=$(tr "$windows_chars" _ <<<"${i,,}")

    # if a directory, make it, instead of copying contents
    if [[ -d $i ]]
    then
        mkdir -p "$prefix$new_name"
        echo mkdir -p "$prefix$new_name"
    else
        # get filename without extension
        name_wo_ext=${new_name%.*}
        # get extension
        # The trick is to make sure that, for:
        # "a.b.c", name_wo_ext is "a.b" and ext is ".c"
        # "abc", name_wo_ext is "abc" and ext is empty
        # Then, we can join the strings without worrying about the
        # . before an extension
        ext=${new_name#$name_wo_ext}
        count=$(find_num_files "$name_wo_ext" "$ext")
        name_wo_ext=$(str_join - "$name_wo_ext" $count)
        cp "$i" "$prefix$name_wo_ext$ext"
        echo cp "$i" "$prefix$name_wo_ext$ext"
    fi
done

動作中:

$ tree a:b
a:b
├── b:c
│   ├── a:d
│   ├── A:D
│   ├── a:d.b
│   └── a:D.b
├── B:c
└── B"c
    └── a<d.b

3 directories, 5 files
$ find a:b -exec ./rename-windows.sh {} +
mkdir -p windows/a_b
mkdir -p windows/a_b/b_c
mkdir -p windows/a_b/b_c
cp a:b/B"c/a<d.b windows/a_b/b_c/a_d.b
mkdir -p windows/a_b/b_c
cp a:b/b:c/a:D.b windows/a_b/b_c/a_d-0.b
cp a:b/b:c/A:D windows/a_b/b_c/a_d
cp a:b/b:c/a:d windows/a_b/b_c/a_d-1
cp a:b/b:c/a:d.b windows/a_b/b_c/a_d-1.b
$ tree windows/
windows/
└── a_b
    └── b_c
        ├── a_d
        ├── a_d-0.b
        ├── a_d-1
        ├── a_d-1.b
        └── a_d.b

2 directories, 5 files

スクリプトは my Github repo で利用可能です。

6
muru

ファイル名の文字列または文字のリストを他の文字列または文字で再帰的に置き換えます

以下のスクリプトを使用して、ファイル名に含まれる可能性のある文字列または文字のリストを、文字列ごとの任意の置換で置き換えることができます。スクリプトはファイル自体の名前を変更するだけで(パスではない)、ディレクトリをいじるリスクはありません。

置換はリストで定義されています:chars(以下を参照)。各文字列に独自の置換を与えて、名前変更を元に戻すことができるようにすることができます。 (置換が一意の文字列であると仮定します)。問題のあるすべての文字列をアンダースコアに置き換える場合は、次のようにリストを定義するだけです。

chars = [
    ("<", "_"),
    (">", "_"),
    (":", "_"),
    ('"', "_"),
    ("/", "_"),
    ("\\", "_"),
    ("|", "_"),
    ("?", "_"),
    ("*", "_"),
    ]

デュープ

名前の重複を防ぐために、スクリプトは最初に「新しい」名前を作成します。次に、同じ名前のファイルが同じディレクトリに既に存在するかどうかを確認します。その場合、ファイルの「使用可能な」新しい名前が見つかるまで、dupe_1またはdupe_2を前に付けた新しい名前を作成します。

enter image description here

になる:

enter image description here

スクリプト

#!/usr/bin/env python3
import os
import shutil
import sys

directory = sys.argv[1]

# --- set replacement below in the format ("<string>", "<replacement>") as below
chars = [
    ("<", "_"),
    (">", "_"),
    (":", "_"),
    ('"', "_"),
    ("/", "_"),
    ("\\", "_"),
    ("|", "_"),
    ("?", "_"),
    ("*", "_"),
    ]
# ---

for root, dirs, files in os.walk(directory):
    for file in files:
        newfile = file
        for c in chars:
            newfile = newfile.replace(c[0], c[1])
        if newfile != file:
            tempname = newfile; n = 0
            while os.path.exists(root+"/"+newfile):
                n = n+1; newfile = "dupe_"+str(n)+"_"+tempname
            shutil.move(root+"/"+file, root+"/"+newfile)

使い方

  1. スクリプトを空のファイルにコピーし、rename_chars.pyとして保存します。
  2. 置換リストが必要な場合は編集します。現状では、scrip0tは問題のある文字をすべてアンダースコアに置き換えますが、選択はあなた次第です。
  3. 次のコマンドを使用して、ディレクトリでテスト実行します。

    python3 /path/to/rename_chars.py <directory_to_rename>
    

注意

次の行に注意してください。

("\\", "_bsl_"),

pythonでは、バックスラッシュを別のバックスラッシュでエスケープする必要があります。

2
Jacob Vlijm