サブパスとリーフノード(ファイル)を最初に使用してディレクトリを繰り返し一覧表示します(部分ファイル名のバッチ名を変更するため)。

サブパスとリーフノード(ファイル)を最初に使用してディレクトリを繰り返し一覧表示します(部分ファイル名のバッチ名を変更するため)。

重複項目として宣言する前に、ファイル名とディレクトリ名に共通の文字列を含むツリー構造の名前を一括変更したり、新しい名前にコピーしたりする特別な理由から、この項目が必要であることを検討してください。これは例です(Ubuntu 14.04で試したのでGNUツールを使用しています)。

cd /tmp
mkdir myproj
mkdir -p myproj/myproj_AA/myproj_BB
touch myproj/myproj_AA/myproj_BB/myproj_CC.dat
mkdir myproj/myproj_AA/myproj_DD
touch myproj/myproj_AA/myproj_DD/myproj_EE.dat
mkdir -p myproj/myproj_XX/myproj_YY
touch myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
mkdir -p myproj/myproj_XX/myproj_WW
touch myproj/myproj_XX/myproj_WW/myproj_QQ.dat
tree myproj # to visualise

ディレクトリ構造はtree次のとおりです。

myproj
├── myproj_AA
│   ├── myproj_BB
│   │   └── myproj_CC.dat
│   └── myproj_DD
│       └── myproj_EE.dat
└── myproj_XX
    ├── myproj_WW
    │   └── myproj_QQ.dat
    └── myproj_YY
        └── myproj_ZZ.dat

6 directories, 4 files

myproj/したがって、(自分を含む)すべての項目の名前を(名前で示される場所ごとに)代わりにmyproj変更したいと思います。したがって、まず現在のディレクトリへの相対パスを含むリストを取得する必要があります。次に、最も外側のサブディレクトリ(これは最も長い相対パス名を持つファイルと同じですがわかりません)が最初のディレクトリになるようにソートする必要があります。 (最初に/ mvディレクトリの名前を変更してからその中のファイルの名前を変更しようとすると、古いディレクトリ名を最初の引数として使用し、名前が変更されたため失敗する可能性があるためです。)myTESTprojmyproj

ls -R --group-directories-first myproj/まず、ディレクトリを再帰的に使用してグループ化する方法を知っていますが、ls結果は次のようになります。

$ ls -R --group-directories-first myproj/
myproj/:
myproj_AA  myproj_XX

myproj/myproj_AA:
myproj_BB  myproj_DD

myproj/myproj_AA/myproj_BB:
myproj_CC.dat

myproj/myproj_AA/myproj_DD:
myproj_EE.dat

myproj/myproj_XX:
myproj_WW  myproj_YY

myproj/myproj_XX/myproj_WW:
myproj_QQ.dat

myproj/myproj_XX/myproj_YY:
myproj_ZZ.dat

...つまり、簡単に提供できるサブパスを含む単純なリストではありません。while read f; do ...

私が得た最も近いのは、以下を使用することですfind

$ find myproj/
myproj/
myproj/myproj_AA
myproj/myproj_AA/myproj_DD
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/myproj_AA/myproj_BB
myproj/myproj_AA/myproj_BB/myproj_CC.dat
myproj/myproj_XX
myproj/myproj_XX/myproj_YY
myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
myproj/myproj_XX/myproj_WW
myproj/myproj_XX/myproj_WW/myproj_QQ.dat

これには単純なサブパスのリストがありますが、ルートノードからリーフノードまでソートされているので、まずリーフノードをソートする必要があります。似たようなことを試していますfind myproj/ | sort -nが違いはないようです。だから私が次のようにすれば:

$ find myproj/ | sort -n | while read f; do mv -v $f $(echo $f | sed 's/myproj/myTESTproj/g'); done
‘myproj/’ -> ‘myTESTproj/’
mv: cannot stat ‘myproj/myproj_AA’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB/myproj_CC.dat’: No such file or directory
...

...その後、ルートノード(ディレクトリ)の名前が最初に変更されるため、予想される再帰の名前変更はすぐに失敗するため、それに対するすべての追加参照は無効です。

では、このようなバッチの名前変更に使用するために、リーフノードを持つサブディレクトリの正しい再帰リストを最初に取得します。

答え1

あなたの目標が名前を変更するだけであれば、ディレクトリ自体の前に各ディレクトリの内容を処理するだけでは十分ではありませんか?みんな葉(みんな目次)まず?find -depthそれがまさにそれがすることです。

$ mkdir -p a/b c/d
$ find -depth
./a/b
./a
./c/d
./c
.

find -execその後、Bashを使用してファイルの名前を変更できます。

$ find -depth ! -name . -name "*myproj*" -execdir bash -c '
    for f; do mv "$f" "${f/myproj/myTESTproj}" ; done' bash {} +

答え2

Perlバージョンのコマンドがインストールされている場合、この方法は機能しますrename(時々prename

find myproj -depth -name '*myproj*' -exec rename -n 's!(.*)myproj!$1myTESTproj!' {} +

この-depthオプションをfind使用すると、ディレクトリ内のサブディレクトリがディレクトリ自体の前に一覧表示されます。ジョブのサフィックスは、指定されたコマンドの単一呼び出しに対する複数の挿入を+許可します。効率が低下した場合に置き換えることができます。-exec{}\;

必要に応じて動作することを確認したら、削除または-n交換してください-v

答え3

質問を投稿した後、何を探すべきかを思い出しました。もしリーフノードは、最も長い相対パス名を持つノードです(常に真であるかどうかはわかりませんが、少なくともOPの例ではそうです)。これにより、文字列のリストを文字列でソートする方法が必要です。残念ながら、sortそのようなオプションはないようです。

しかし、私は発見したhttps://stackoverflow.com/questions/5917576/sort-a-text-file-by-line-length-include-spaces- 次に、そこからperlソリューションを選択します。

$ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>'
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/myproj_AA/myproj_BB/myproj_CC.dat
myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
myproj/myproj_XX/myproj_WW/myproj_QQ.dat
myproj/myproj_AA/myproj_DD
myproj/myproj_AA/myproj_BB
myproj/myproj_XX/myproj_YY
myproj/myproj_XX/myproj_WW
myproj/myproj_AA
myproj/myproj_XX
myproj/

しかし、sed 's/myproj/myTESTproj/g'ここでも単純な置換は機能しません。

$ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>' \
> | while read f; do mv -v $f $(echo $f | sed 's/myproj/myTESTproj/g'); done
‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ -> ‘myTESTproj/myTESTproj_AA/myTESTproj_DD/myTESTproj_EE.dat’
mv: cannot move ‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ to ‘myTESTproj/myTESTproj_AA/myTESTproj_DD/myTESTproj_EE.dat’: No such file or directory
...

...だから私たちはsed 1行の最後の項目のみを置き換える、今すぐsed -E 's/(.*)myproj/\1myTESTproj/g'

$ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>' \
| while read f; do mv -v $f $(echo $f | sed -E 's/(.*)myproj/\1myTESTproj/g'); done
‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ -> ‘myproj/myproj_AA/myproj_DD/myTESTproj_EE.dat’
‘myproj/myproj_AA/myproj_BB/myproj_CC.dat’ -> ‘myproj/myproj_AA/myproj_BB/myTESTproj_CC.dat’
‘myproj/myproj_XX/myproj_YY/myproj_ZZ.dat’ -> ‘myproj/myproj_XX/myproj_YY/myTESTproj_ZZ.dat’
‘myproj/myproj_XX/myproj_WW/myproj_QQ.dat’ -> ‘myproj/myproj_XX/myproj_WW/myTESTproj_QQ.dat’
‘myproj/myproj_AA/myproj_DD’ -> ‘myproj/myproj_AA/myTESTproj_DD’
‘myproj/myproj_AA/myproj_BB’ -> ‘myproj/myproj_AA/myTESTproj_BB’
‘myproj/myproj_XX/myproj_YY’ -> ‘myproj/myproj_XX/myTESTproj_YY’
‘myproj/myproj_XX/myproj_WW’ -> ‘myproj/myproj_XX/myTESTproj_WW’
‘myproj/myproj_AA’ -> ‘myproj/myTESTproj_AA’
‘myproj/myproj_XX’ -> ‘myproj/myTESTproj_XX’
‘myproj/’ -> ‘myTESTproj/’
$ tree myTESTproj/
myTESTproj/
├── myTESTproj_AA
│   ├── myTESTproj_BB
│   │   └── myTESTproj_CC.dat
│   └── myTESTproj_DD
│       └── myTESTproj_EE.dat
└── myTESTproj_XX
    ├── myTESTproj_WW
    │   └── myTESTproj_QQ.dat
    └── myTESTproj_YY
        └── myTESTproj_ZZ.dat

6 directories, 4 files

これが私の要件を満たしていると思います。しかし、最も長いパス名==リーフファイルノードであるという仮定が常に正しいかどうかはわかりません。


編集:次の構造の場合は間違いなく失敗します。

myproj/somespecdir/someotherdir/myproj_CC.dat
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/somespecdir/someotherdir
myproj/myproj_AA/myproj_DD
myproj/somespecdir
myproj/myproj_AA
myproj/

...つまり、名前が変更されたパスから検索され、変更される部分文字列の最初の項目も最後の(固有の)項目であり、その部分文字列が複数回表示されるリストのパスの前に表示されます。

関連情報