ディレクトリをフラット化し、新しいファイル名でディレクトリ名を保持


11

次の形式でディレクトリをフラット化するにはどうすればよいですか?

前: ./aaa/bbb/ccc.png

後: ./aaa-bbb-ccc.png


PS:奇数文字(スペースなど)が含まれるファイル名も考慮してください
Nathan JB

3
また./foo/bar/baz.png./foo-bar-baz.png両方が存在する可能性のあるケースをどのように処理するかを指定する必要もあります。後者を前者に置き換えたくないと思いますか?
jw013

私の1つのケースでは、それは無関係ですが、答えは確かにそれを考慮に入れて、他の人が信頼できるようにする必要があります。ありがとう!
ネイサンJB

回答:


7

警告:これらのコマンドのほとんどをブラウザに直接入力しました。警告講師。

zshおよびzmvの場合

zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'

説明:パターン**/*は、現在のディレクトリのサブディレクトリにあるすべてのファイルに再帰的に一致します(現在のディレクトリにあるファイルには一致しませんが、これらの名前を変更する必要はありません)。括弧の最初の二対として参照されることができる基であり、$1そして$2置換テキストに。括弧の最後のペアはD glob修飾子を追加して、ドットファイルが省略されないようにします。既存のファイルが上書きされるかどうかを尋ねるようにオプション-o -iを渡すことを意味します。-imv


POSIXツールのみ:

find . -depth -exec sh -c '
    for source; do
      case $source in ./*/*)
        target="$(printf %sz "${source#./}" | tr / -)";
        mv -i -- "$source" "${target%z}";;
      esac
    done
' _ {} +

説明:このcaseステートメントは、現在のディレクトリと現在のディレクトリの最上位のサブディレクトリを省略しています。targetソースファイル名($0)が含まれ、先頭の./文字が取り除かれ、すべてのスラッシュがダッシュに置き換えられ、最後にが含まれzます。最後zは、ファイル名が改行で終わっている場合に存在します。それ以外の場合、コマンド置換はそれを取り除きます。

あなたfindがサポートしていない場合-exec … +(OpenBSD、私はあなたを見ています):

find . -depth -exec sh -c '
    case $0 in ./*/*)
      target="$(printf %sz "${0#./}" | tr / -)";
      mv -i -- "$0" "${target%z}";;
    esac
' {} \;

bash(またはksh93)では、スラッシュをダッシュ​​に置き換えるために外部コマンドを呼び出す必要はありません。ksh93パラメーター拡張をストリング置換構成で使用できます${VAR//STRING/REPLACEMENT}

find . -depth -exec bash -c '
    for source; do
      case $source in ./*/*)
        source=${source#./}
        target="${source//\//-}";
        mv -i -- "$source" "$target";;
      esac
    done
' _ {} +

for sourceあなたは(通常は)使用している理由としては、最初に提示したファイル/ディレクトリを欠場...私は最終的に出て発見した_として$0:) ...と偉大な答えてくれてありがとう。私は彼らから多くを学びます。
Peter.O 2012

zmvの例は、予行演習のみを行うことに注意してください。移動コマンドは出力されますが、実行されません。これらのコマンドを実際に実行するには、-Qnのnを削除します。
Peter

5

ここには良い答えがありますが、これはより直感的ですbash

find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;

aaa既存のディレクトリはどこですか。そこに含まれるファイルは現在のディレクトリに移動されます(aaaには空のディレクトリのみが残ります)。trディレクトリとスペースの両方を処理するための2つの呼び出し(エスケープされたスラッシュのロジックなし-読者のための練習)。

これはより直感的で、調整が比較的簡単だと思います。findつまり、.pngファイルのみを検索する場合は、パラメータを変更できます。tr通話を変更したり、必要に応じて追加したりできます。そして、それが正しいことを視覚的に確認したい場合は、echo前に追加できますmv(次にecho、物事が正しく見える場合にのみ、追加なしで繰り返します:

find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;

また、find aaa...と...ではなくfind .... を使用していることに注意してくださいfind aaa/。後者の2つは、最終的なファイル名に面白いアーティファクトを残すためです。


この1つのライナーに感謝します。これは、ディレクトリの外側から実行したときにうまく機能しました(これはまさにあなたが言ったことです)。(ルートディレクトリ名が追加されないようにするために)ディレクトリ内から実行しようとすると、すべてのファイルが「消えた」。それらを同じディレクトリの隠しファイルに変換しました。
dgig 2018

2
find . -mindepth 2 -type f -name '*' |
  perl -l000ne 'print $_;  s/\//-/g; s/^\.-/.\// and print' |
    xargs -0n2 mv 

注:これは、を含むファイル名では機能しません\n
もちろん、これはタイプfファイルのみを移動します...
名前の衝突は、pwdに既存のファイルからのみです。

この基本的なサブセットでテスト済み

rm -fr junk
rm -f  junk*hello*

mkdir -p  junk/junkier/junkiest
touch    'hello    hello'
touch    'junk/hello    hello'
touch    'junk/junkier/hello    hello'
touch    'junk/junkier/junkiest/hello    hello'

その結果

./hello    hello
./junk-hello    hello
./junk-junkier-hello    hello
./junk-junkier-junkiest-hello    hello

0

これはlsバージョンです。コメントは歓迎します。このような奇抜なファイル名でも機能します "j1ळ\n\001\n/j2/j3/j4/\nh  h\n"が、落とし穴を知りたいと思っています。それは「悪意のある人がls実際に(頑強に)できるか」の練習問題です。

ls -AbQR1p * |                        # paths in "quoted" \escaped format
    sed -n '/":$/,${/.[^/]$/p}' |     # skip files in pwd, blank and dir/ lines
    { bwd="$PWD"; while read -n1;     # save base dir strip leading "quote
                        read -r p; do # read path
        printf -vs "${p%\"*}"         # strip trailing quote"(:)
        [[ $p == *: ]] && {           # this is a directory
            cd   "$bwd/$s"            # change into new directory
            rnp="${s//\//-}"-         # relative name prefix
        } || mv "$s" "$bwd/$rnp$s"
      done; }

-1

ファイル名+パスをtrコマンドにパイプするだけです。tr <replace> <with>

for FILE in `find aaa -name "*.png"`
do
  NEWFILE=`echo $FILE | tr '/' '-'`
  cp -v $FILE $NEWFILE
done

1
これは奇妙なファイル名を安全に処理しません。スペースは(特に)それを壊します。
jw013

一見すると、これはクリーンで読みやすいソリューションですが、@ jw013は正しいので、スペースをうまく処理できません。助けるためにcp行を変更しcp -v "$FILE" "$NEWFILE"ますか?(また、pngファイルのみを想定するべきではありません。)
Nathan JB '16

2
@ NathanJ.Brauer cp行に引用符を追加するだけでは不十分です。ループを使用してfind出力を解析するアプローチ全体にfor欠陥があります。使用する唯一の安全な方法がfindである-exec、または-print0(より少ないポータブル可能な場合-exec)。より堅牢な代替案をお勧めしますが、あなたの質問は現時点では不十分です。
jw013

-2

ファイル名のスペースに安全:

#!/bin/bash

/bin/find $PWD -type f | while read FILE
do
    _new="${FILE//\//-}"
    _new="${_new:1}"
    #echo $FILE
    #echo $_new

    /bin/mv "$FILE" "$_new"
done

明らかな理由でではcpなくを使って鉱山を走らmvせましたが、同じものを生産するはずです。


3
type find-> find is /usr/bin/find私のマシンで。PATHスクリプトで変数名として使用するのはひどい考えです。また、readコマンドを誤用しているため、スクリプトはファイル名をスペースまたはバックスラッシュでマングルします(このサイトでを検索してくださいIFS read -r)。
Gilles「SO-邪悪なことをやめなさい」

find is hashed (/bin/find)、関連する方法?異なるディストリビューションは異なりますか?
Tim

ハゲタカの餌はこれから遠ざけておくべきだった。
Tim

@ティムあなたはいつでもあなたの担当者を取り戻すために答えを削除することができます(そして私と同様にそれは担当者が反対投票するのに費用がかかります)。スクリプトへのパスのハードコーディングは、PATH環境変数のポイントが不足していることを示唆しているようです。これは、すべての Unixシステムが使用しているものです。あなたが書いた/bin/find場合、スクリプトは実際にはないすべてのシステム(Gilles、mine、おそらくOP)で壊れています/bin/find(たとえば、b / cはにあります/usr/bin/find)。PATH環境変数の要点は、これを問題にしないことです。
jw013

ちなみに、$(pwd)は変数で既に使用可能です:$PWD。ただし、ここでは絶対パスを使用しないでください。たとえば、スクリプトがから呼び出された場合、スクリプトは/home/timに名前/home/tim/some/pathを変更しようとし/home-tim-some-pathます。
Gilles「SO-悪をやめる」
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.