bashスクリプトを使用してファイル名のスペースを置き換える方法


264

特定のルートディレクトリから始まるファイルおよびディレクトリ名のスペースをアンダースコアで再帰的に置き換える安全なソリューションを誰かが推奨できますか?例えば:

$ tree
.
|-- a dir
|   `-- file with spaces.txt
`-- b dir
    |-- another file with spaces.txt
    `-- yet another file with spaces.pdf

になる:

$ tree
.
|-- a_dir
|   `-- file_with_spaces.txt
`-- b_dir
    |-- another_file_with_spaces.txt
    `-- yet_another_file_with_spaces.pdf

9
と呼ばれるfoo barファイルとfoo_bar同じディレクトリに別のファイルが呼び出された場合、どうしますか?
マークバイアーズ

良い質問。既存のファイルを上書きしたり、データを失いたくない。変更しないでおく必要があります。警告を出力するのが理想的ですが、それはおそらく要求が多すぎます。
armandino 2010

回答:


312

rename(別名prename)を使用します。これは、システムにすでに存在している可能性があるPerlスクリプトです。2つのステップで実行します。

find -name "* *" -type d | rename 's/ /_/g'    # do the directories first
find -name "* *" -type f | rename 's/ /_/g'

ユルゲンの回答に基づいており、「/usr/bin/renamePerlスクリプト」の「Revision 1.5 1998/12/18 16:16:31 rmb1」バージョンを使用して、単一の境界でファイルとディレクトリの複数のレイヤーを処理できます。

find /tmp/ -depth -name "* *" -execdir rename 's/ /_/g' "{}" \;

5
2段階の必要はありません:使用は深さ優先探索:DIR -depth見つける
ユルゲン・Hötzel

3
ああ、私はrenameマンページを読んだところです(私はツールを知りませんでした)。コードs/ /_/gy/ /_/;-)に変更することでコードを最適化できると思います
Michael Krelin-ハッカー

1
もちろん、そこからパフォーマンスを向上させることはできません。それは、適切なツールを使用することについてです。そして、この全体の質問は多かれ少なかれマイクロ最適化についてです。結局楽しいじゃないですか。;-)
マイケルクレリン-ハッカー、

14
OS Xでこれを実行している場合は、次のことを行う必要がありますbrew install rename
loeschg

7
これはCentos 7では機能しません。renameコマンドは完全に異なり(perlスクリプトではなくバイナリーです)、stdinからのデータを受け入れません。
CpnCrunch 2015年

357

私が使う:

for f in *\ *; do mv "$f" "${f// /_}"; done

再帰的ではありませんが、非常に高速でシンプルです。私はここの誰かが再帰的にそれを更新できると確信しています。

${f// /_}一部には、与えられた文字列でパラメータ内のパターンを交換するためのbashのパラメータ膨張機構を利用しています。関連する構文は${parameter/pattern/string}です。参照:https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.htmlまたはhttp://wiki.bash-hackers.org/syntax/pe


9
シンプルでMacで動作します。(macにrename
はがなく

6
素晴らしい答え。for d in *\ *; do mv "$d" "${d// /}"; doneアンダーアンダースコアを使用しました。
ユン・リー

1
「検索-名前」の回答とは異なり、これは私のOS Xで動作しました!ありがとうございます!
Julio Faerman、2016年

1
参考までに、これはbashでとを使用すると簡単再帰的にshopt -s globstarなりfor f in **/*\ *; do ...ます。globstar一方のオプションは、bashの内部にありrename、コマンドが一般的なLinuxのツールではなくbashのの一部です。
ghoti

2
${f// /_}あるバッシュ変数の拡張のための検索と置換。- は、スペースを含む各ファイルfforループからの変数です。-1つ目//は「すべて置換」を意味します(最初に停止しないでください)。-次に、「/ _」は「スペースをアンダースコアに置き換える」を意味します
アリ

101
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done

私はディレクトリを考えていなかったので、最初は正しく理解できませんでした。


1
魅力のように機能します。マイケルに感謝します。
armandino 2010

デニス、しっかりキャッチ、IFS=''前に置くことで簡単に修正read。また、私が他のコメントでわかることについては、へsort-depthオプションを優先してステップを削除できますfind
マイケルクレリン-ハッカー2010年

1
ファイル名に\ (バックスラッシュ)が含まれている場合は機能しません。-r読み取るオプションを追加することで修正できます。
jfg956 2013年

8
ソリューションをコピーして使用するために、このページにアクセスするのは今回で50回目です。ありがとう非常に。私はMacを使用していて、renameDennisによって提案されたコマンドがないので、私はあなたの答えを好みます。
Alex Constantin

@AlexConstantin、macports持っていないrename?タスクが実用性を正当化するとは思わないので、私は気にしたことはありません。ない場合はmacports、インストールすることを検討してください;)
Michael Krelin-ハッカー

30

あなたはdetoxダグハープルで使用できます

detox -r <folder>

12

検索/リネームソリューション。renameはutil-linuxの一部です。

空白のファイル名は空白のディレクトリの一部になる可能性があるため、最初に深さを下る必要があります。

find /tmp/ -depth -name "* *" -execdir rename " " "_" "{}" ";"

私があなたのものを走らせたとき、私はまったく変化がありません。
追って通知があるまで一時停止。

チェックインのutil-linuxのセットアップ:$名前変更--version名前変更(UTIL-のlinux-ngの2.17.2)
ユルゲンHötzel

/ usr / bin / rename(Perlスクリプト)を確認すると、「Revision 1.5 1998/12/18 16:16:31 rmb1」
通知があるまで

うーん...あなたのutil-linuxバイナリはどこに行ったの?このファイルパスはutil-linuxが所有する必要があります。私たちはGNU-Linuxシステムではないのですか?
ユルゲンヘッツェル2010

1
これは私の実行で1つのスペースのみを変更するので、「山に火をつけろ」は「山に火をつけろ」になります。
brokkr 2012年

6

bash 4.0

#!/bin/bash
shopt -s globstar
for file in **/*\ *
do 
    mv "$file" "${file// /_}"       
done

ファイル名またはディレクトリ名にスペースがない場合、mvはそれ自体にmvを実行するように見えます(mv:a' to a subdirectory of itself, a / a 'を移動できません)
armandino

関係ない。にリダイレクトして、エラーメッセージを削除してください/dev/null
ghostdog74 2010

ゴーストドッグ、mv5つのファイルをリネームするためだけに55万回スポーンすることは、ユーザーをメッセージで溢れさせなくても、少しオーバーヘッドになるかもしれません。
マイケルクレリン-ハッカー、

krelin、findでも、あなたが言及した55000ファイルを調べてスペースのあるファイルを見つけ、名前を変更します。バックエンドでは、まだすべてを通過しています。必要に応じて、名前を変更する前にスペースを最初にチェックすることで実行できます。
ghostdog74 2010

私は、mvのスポーンについて話していました。ではないだろうfor file in *' '*か、そのようないくつかは、より良い仕事をしますか?
マイケルクレリン-ハッカー、

6

あなたはこれを使うことができます:

    find . -name '* *' | while read fname 

do
        new_fname=`echo $fname | tr " " "_"`

        if [ -e $new_fname ]
        then
                echo "File $new_fname already exists. Not replacing $fname"
        else
                echo "Creating new file $new_fname to replace $fname"
                mv "$fname" $new_fname
        fi
done

3

Naidim's Answersの再帰バージョン。

find . -name "* *" | awk '{ print length, $0 }' | sort -nr -s | cut -d" " -f2- | while read f; do base=$(basename "$f"); newbase="${base// /_}"; mv "$(dirname "$f")/$(basename "$f")" "$(dirname "$f")/$newbase"; done

2

これは、(非常に詳細な)find -execソリューションで、「ファイルはすでに存在します」という警告をstderrに書き込みます。

function trspace() {
   declare dir name bname dname newname replace_char
   [ $# -lt 1 -o $# -gt 2 ] && { echo "usage: trspace dir char"; return 1; }
   dir="${1}"
   replace_char="${2:-_}"
   find "${dir}" -xdev -depth -name $'*[ \t\r\n\v\f]*' -exec bash -c '
      for ((i=1; i<=$#; i++)); do
         name="${@:i:1}"
         dname="${name%/*}"
         bname="${name##*/}"
         newname="${dname}/${bname//[[:space:]]/${0}}"
         if [[ -e "${newname}" ]]; then
            echo "Warning: file already exists: ${newname}" 1>&2
         else
            mv "${name}" "${newname}"
         fi
      done
  ' "${replace_char}" '{}' +
}

trspace rootdir _

2

これはもう少し機能します。ダウンロードした急流の名前を変更するために使用します(特殊文字(ASCII以外)なし、スペース、複数のドットなど)。

#!/usr/bin/perl

&rena(`find . -type d`);
&rena(`find . -type f`);

sub rena
{
    ($elems)=@_;
    @t=split /\n/,$elems;

    for $e (@t)
    {
    $_=$e;
    # remove ./ of find
    s/^\.\///;
    # non ascii transliterate
    tr [\200-\377][_];
    tr [\000-\40][_];
    # special characters we do not want in paths
    s/[ \-\,\;\?\+\'\"\!\[\]\(\)\@\#]/_/g;
    # multiple dots except for extension
    while (/\..*\./)
    {
        s/\./_/;
    }
    # only one _ consecutive
    s/_+/_/g;
    next if ($_ eq $e ) or ("./$_" eq $e);
    print "$e -> $_\n";
    rename ($e,$_);
    }
}

1

私はこのスクリプトの周りを見つけました、それは興味深いかもしれません:)

 IFS=$'\n';for f in `find .`; do file=$(echo $f | tr [:blank:] '_'); [ -e $f ] && [ ! -e $file ] && mv "$f" $file;done;unset IFS

名前に改行が含まれているファイルは失敗します。
ghoti 2016


1

macOSを使用してこれで苦労している人のために、最初にすべてのツールをインストールします:

 brew install tree findutils rename

次に、名前を変更する必要がある場合は、GNU find(gfind)のエイリアスをfindにします。次に、@ Michel Krelinのコードを実行します。

alias find=gfind 
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done   

0

これにより、現在のディレクトリ内のファイルのみが検索され、名前が変更されます。これにはエイリアスがあります。

find ./ -name "* *" -type f -d 1 | perl -ple '$file = $_; $file =~ s/\s+/_/g; rename($_, $file);


0

私は自分の目的のために作るだけです。参考にしてください。

#!/bin/bash
cd /vzwhome/c0cheh1/dev_source/UB_14_8
for file in *
do
    echo $file
    cd "/vzwhome/c0cheh1/dev_source/UB_14_8/$file/Configuration/$file"
    echo "==> `pwd`"
    for subfile in *\ *; do [ -d "$subfile" ] && ( mv "$subfile" "$(echo $subfile | sed -e 's/ /_/g')" ); done
    ls
    cd /vzwhome/c0cheh1/dev_source/UB_14_8
done

0

/ filesという名前のフォルダー内のファイルの場合

for i in `IFS="";find /files -name *\ *`
do
   echo $i
done > /tmp/list


while read line
do
   mv "$line" `echo $line | sed 's/ /_/g'`
done < /tmp/list

rm /tmp/list

0

この問題に対する私の解決策はbashスクリプトです。

#!/bin/bash
directory=$1
cd "$directory"
while [ "$(find ./ -regex '.* .*' | wc -l)" -gt 0 ];
do filename="$(find ./ -regex '.* .*' | head -n 1)"
mv "$filename" "$(echo "$filename" | sed 's|'" "'|_|g')"
done

スクリプトを実行した後、引数として、スクリプトを適用するディレクトリ名を入力するだけです。


0

macOS

選ばれた答えのように。

brew install rename

# 
cd <your dir>
find . -name "* *" -type d | rename 's/ /_/g'    # do the directories first
find . -name "* *" -type f | rename 's/ /_/g'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.