私は、次のディレクトリがあるとしましょう。
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
現在のパスの下のすべてのディレクトリを見つけるために使用されます。-print0
read
スペースを含めることができるディレクトリに対しても find 出力を処理するために一緒に使用されます。grep -o
パスからスラッシュを選択するために使用されます。wc -l
スラッシュ数を計算するために使用されます。sort
tail
スラッシュが最も多いパスを選択します。cut
スラッシュを削除し、最も深いディレクトリへのパスのみを表示するために使用されます。
答え3
これはbash中心のバージョンです。次のbash機能を使用してください。
- globstar シェルオプションディレクトリとサブディレクトリ拡張の有効化
**/
- バックアップ配列の内容を読むディレクトリ深度の計算
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"
}
この関数は次のことを行います。
- globstar シェルオプションが設定されていることを確認します。それ以外の場合は、最後にオプションをリセットするためのフラグを保存します。
- 現在知られている最も深いディレクトリとそのレベル(
$1
およびそれぞれ1
)を初期化します。 - 指定されたパラメータの下の各サブディレクトリを展開し、それを繰り返します。
- 各サブディレクトリに対して;で区切られた配列として読み込みます
/
。配列の要素数を計算し、現在知られている最も深いディレクトリレベルと比較します。さらに深くなったら、これらの変数をリセットしてください。 - 最も深いサブディレクトリがあれば
cd
それはすべてです。 - 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
最初の方法 - 再帰、私の考えに最適な方法です。
使用法:rcd
bashに関数をロードし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
)をにロードします。関数は同じことを行いますが、異なる方法で実装されています。bash
source 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