ディレクトリの最も深いサブディレクトリに移動するターミナルコマンドはありますか?

ディレクトリの最も深いサブディレクトリに移動するターミナルコマンドはありますか?

私は、次のディレクトリがあるとしましょう。

Dropbox(フォルダ)

--->ご飯(フォルダ)

--------> 2017(フォルダ)

------------>画像(フォルダ)

---> image.png (ファイル)

これは可能ですが、 cd Dropbox最も深いディレクトリに手動で移動する必要がありますimages

cd Dropbox:deepest directory私をそこに連れて行くためのそのような命令がありますか Dropbox/Bob/2017/images

あるレベルでタイがあればそのレベルで停止

答え1

そしてzsh

bydepth() REPLY=${REPLY//[^\/]}
cd Dropbox/**/*(D/O+bydepth[1])

bydepth削除されていない文字を含むファイルを返すソート関数を定義します/(したがって、変換された順序は深くなります)。**/glob修飾子で再帰的globbingを使用します(サブディレクトリのすべてのレベル)。

  • Dまた、ディレクトリを隠すことも検討してください。
  • /ディレクトリにのみ適用
  • O+bydepth:深さに応じて逆順に並べ替え
  • [1](ソート後)最初の項目のみを取得します。

GNUツールを使用すると、bashこれは次のようになります。

IFS= read -rd '' deepest < <(find Dropbox/ -type d -print0 |
  awk -v RS='\0' -v ORS='\0' -F / '
    NF > max {max = NF; deepest = $0}
    END {if (max) print deepest}') && cd -- "$deepest"

(同点の場合、選択した方法が必ずしもzsh方法と同じである必要はありません。)

新しい追加要件により、状況がさらに複雑になりました。基本的に、私が正しく理解した場合、リンクの場合は、最大深度のすべてのディレクトリの最も深い共通の親ディレクトリに変更する必要があります。そしてzsh

cd_deepest() {
  setopt localoptions rematchpcre
  local REPLY dirs result dir match
  dirs=(${1:-.}/**/*(ND/nOne:'
   REPLY=${#REPLY//[^\/]}-$REPLY':))
  (($#dirs)) || return
  result=$dirs[1]
  for dir ($dirs[2,-1]) {
    [[ $result//$dir =~ '^([^-]*-.*)/.*//\1/' ]] || break
    result=$match[1]
  }
  cd -- ${result#*-} && print -rD -- $PWD
}

例:

$ tree Dropbox
Dropbox
├── a
│   └── b
│       ├── 1
│       │   └── x
│       └── 2
│           └── x
└── c
    └── d
        └── e

9 directories, 0 files
$ cd_deepest Dropbox
~/Dropbox/a/b

(最も深いDropbox/a/b/1/x場合は最も深い共通上位()Dropbox/a/b/2/xに変更します。)Dropbox/a/b

答え2

find . -type d -print0 | while IFS= read -r -d $'\0' line; do echo -n $(echo "$line" | grep -o '/' | wc -l); echo " $line"; done | sort | tail -1 | cut -d' ' -f2-

macOS(bash)とArch Linux(zshとbash)でテストされました。

  • find . -type d現在のパスの下のすべてのディレクトリを見つけるために使用されます。
  • -print0readスペースを含めることができるディレクトリに対しても find 出力を処理するために一緒に使用されます。
  • grep -oパスからスラッシュを選択するために使用されます。
  • wc -lスラッシュ数を計算するために使用されます。
  • sorttailスラッシュが最も多いパスを選択します。
  • cutスラッシュを削除し、最も深いディレクトリへのパスのみを表示するために使用されます。

答え3

これはbash中心のバージョンです。次のbash機能を使用してください。

cdd() {
  local _cdd_unset_globstar=0
  shopt -q globstar || _cdd_unset_globstar=1
  shopt -s globstar
  local _cdd_deepest=$1
  local _cdd_level=1
  local _cdd_array=()
  for d in "${1}/"**/
  do
    IFS=/ read -r -d '' -a _cdd_array <<< "$d" || true
    if [ "${#_cdd_array[*]}" -gt "$_cdd_level" ]
    then
      _cdd_deepest=$d
      _cdd_level=${#_cdd_array[*]}
    fi
  done
  cd -- "$_cdd_deepest" && true
  local _cdd_ret="$?"
  [ "$_cdd_unset_globstar" -eq 1 ] && shopt -u globstar
  return "$_cdd_ret"
}

この関数は次のことを行います。

  1. globstar シェルオプションが設定されていることを確認します。それ以外の場合は、最後にオプションをリセットするためのフラグを保存します。
  2. 現在知られている最も深いディレクトリとそのレベル($1およびそれぞれ1)を初期化します。
  3. 指定されたパラメータの下の各サブディレクトリを展開し、それを繰り返します。
  4. 各サブディレクトリに対して;で区切られた配列として読み込みます/。配列の要素数を計算し、現在知られている最も深いディレクトリレベルと比較します。さらに深くなったら、これらの変数をリセットしてください。
  5. 最も深いサブディレクトリがあればcdそれはすべてです。
  6. globstarシェルオプションをリセットする必要がある場合は、そうします。

シェルオプションを設定するためにサブシェルを使用する方がよりきれいであると思われる場合は、それを処理するために2つの関数、つまり上記のタスクを実行するラッパーとサブシェル呼び出し関数を使用できます。

cdd_helper() (
  shopt -s globstar
  _cdd_deepest=$1
  _cdd_level=1
  for d in "${1}/"**/
  do
    IFS=/ read -r -d '' -a _cdd_array <<< "$d" || true
    if [ "${#_cdd_array[*]}" -gt "$_cdd_level" ]
    then
      _cdd_deepest=$d
      _cdd_level=${#_cdd_array[*]}
    fi
  done
  printf "%s" "$_cdd_deepest"
)

cdd() {
  cd -- "$(cdd_helper "$1")"
}

答え4

最初の方法 - 再帰、私の考えに最適な方法です。

使用法:rcdbashに関数をロードしsource recur_cd.shてテストしますrcd some_dir

rcd () {
    if [ ! -d "$*" ]; then
        return 121 
    else
        rcd "${*}"*/
    fi  

    if (($? == 121)); then 
        cd "$*" 
    fi
}

2番目の方法 - findコマンドです(ここには2つのバリエーションがあります)。

使用法:まず、2つの組み込み関数(dcd1およびdcd2)をにロードします。関数は同じことを行いますが、異なる方法で実装されています。bashsource deep_cd.sh

それからテストしてみてください。

dcd1 dir   # first variant
pwd
cd -       # return back for further testing`<br>
dcd2 dir   # second variant
pwd
           # and so on.

deepcd.sh

#!/bin/bash

# first variant, support filenames with spaces, work by awk mainly 
dcd1 () {
    cd "$(find "$1" -type d -printf "%d %p\n" | sort | awk '{
        a[$1]++; 
        tmp_num=$1;
        sub("^[0-9]* ", "", $0);
        path = $0;
        if (a[tmp_num] == 2) { sub("[^/]*$", ""); path = $0; exit; }
    }
    END { print path; }')"
}

# second variant, support filenames with spaces - more programs 
# used (cut, uniq, tail), awk only prints the path, doesn't search it
dcd2 () {
    str=$(find "$1" -type d -printf "%d %p\n" | sort)
    num=$(cut -d " " -f 1 <<< "$str" | uniq -u | tail -n 1)
    path=$(awk -v n=$num 'NR == n+1 { $1=""; print $0; }' <<< "$str")
    cd "${path# }"
}

3番目の方法 - readlineマクロを使用します。

bind '"\e\C-i":"\C-i\C-i\C-i\C-i"'それだけです:)

説明する:

  • bind- 読むhelp bind。キーバインディングに使用されるreadline関数。
  • \e\C-i- Ctlr+ Alt+ i- 押すと、複数のディレクトリが表示される前に自動的にシリーズを完成させる新しいキーの組み合わせです。
  • \C-iと同じで、Tab「完了」を意味します。\C-i考えるほどのディレクトリの深さが必要です。 10個のレベルがあると仮定すると、10個の\C-iブロックが必要です。ただし、3 または 4 のような中間ブロックの数を選択し、必要に応じてキーの組み合わせをダブルAlt + Ctrl + iクリックする方が便利です。これは出力の重複を防ぎます(参照:「試験」部分)

この動作を永久にするには、次の行を追加します。.bashrc

テスト

cd o                        # then Alt + Ctrl + i
cd one/two/three/four/      # directory has changed to deepest

cd A                        # then Alt + Ctrl + i
cd A/B/C/                   # directory has changed to deepest
D/ E/                       # and two inner directories appear
~/deepest_dir$ cd A/B/C/    # one drawback here - if deepest 
D/ E/                       # directory is reached, then completion
~/deepest_dir$ cd A/B/C/    # works idle and print duplicating
                            # output, so may be worth shrinks 'C-i'
                            # blocks amount in the binding string

関連情報