디렉토리의 가장 깊은 하위 디렉토리로 이동하는 터미널 명령이 있습니까?

디렉토리의 가장 깊은 하위 디렉토리로 이동하는 터미널 명령이 있습니까?

내 말은, 다음 디렉토리가 있다고 가정 해 봅시다

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 쉘 옵션을 재설정해야 한다면 그렇게 하십시오.

쉘 옵션을 설정하기 위해 서브쉘을 사용하는 것이 더 깔끔하다고 생각되면 이를 처리하기 위해 두 가지 함수, 즉 위의 작업을 수행하는 래퍼와 서브쉘 호출 함수를 사용할 수 있습니다.

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
}

두 번째 방법 - find 명령입니다(여기에는 두 가지 변형이 있습니다).

용법:먼저 두 개의 내장 함수( dcd1dcd2)를 에 로드합니다. 함수는 동일한 작업을 수행하지만 다르게 구현됩니다.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# }"
}

세 번째 방법 - 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

관련 정보