ฉันจะ cd ไปยังไดเรกทอรี sibling ก่อนหน้า / ถัดไปได้อย่างไร


10

ฉันมักจะมีรูปแบบไดเรกทอรีโครงการเช่นนี้

project
`-- component-a
|   `-- files...
`-- component-b
|   `-- files...
`-- component-c
    `-- files...

ฉันมักจะทำงานในหนึ่งในcomponentไดเรกทอรีเพราะนั่นคือที่ที่ไฟล์อยู่ เมื่อฉันกลับไปที่เชลล์ฉันมักจะต้องย้ายไปยังไดเรกทอรี sibling โดยเฉพาะอย่างยิ่งเมื่อฉันจำเป็นต้องทำการเปลี่ยนแปลงที่ไม่สามารถเขียนสคริปต์ได้ทุกองค์ประกอบ ในกรณีเหล่านั้นฉันจะไม่สนใจสิ่งที่ไดเรกทอรีพี่น้องก่อนหน้าคือที่ฉันจะทำงานหรือไดเรกทอรีพี่น้องถัดไป

ฉันสามารถกำหนดคำสั่งprevหรือnextว่าcdฉันจะเข้าไปในไดเรกทอรีก่อนหน้าหรือไดเรกทอรีถัดไป (ตามตัวอักษรหรืออะไรก็ตาม) ได้อย่างง่ายดายหรือไม่? เพราะการพิมพ์cd ../com<TAB><Arrow keys>ตลอดเวลากำลังจะแก่ลงสักหน่อย

คำตอบ:


8

อย่าใช้คำสั่ง commandlinefu จากคำตอบอื่น ๆ : มันไม่ปลอดภัย¹และไม่มีประสิทธิภาพ²หากคุณใช้bashให้ใช้ฟังก์ชันต่อไปนี้แทน .bashrcจะทำให้พวกเขาถาวรทำให้พวกเขาเป็นของคุณ โปรดทราบว่าฉันใช้ลำดับ glob เพราะในตัวและง่าย โดยทั่วไปแล้วคำสั่ง glob คือตัวอักษรในพื้นที่ส่วนใหญ่แม้ว่า คุณจะได้รับข้อความแสดงข้อผิดพลาดหากไม่มีไดเรกทอรีถัดไปหรือก่อนหน้า โดยเฉพาะอย่างยิ่งที่คุณจะเห็นข้อผิดพลาดหากคุณพยายามnextหรือขณะที่อยู่ในไดเรกทอรีรากprev/

## bash and zsh only!
# functions to cd to the next or previous sibling directory, in glob order

prev () {
    # default to current directory if no previous
    local prevdir="./"
    local cwd=${PWD##*/}
    if [[ -z $cwd ]]; then
        # $PWD must be /
        echo 'No previous directory.' >&2
        return 1
    fi
    for x in ../*/; do
        if [[ ${x#../} == ${cwd}/ ]]; then
            # found cwd
            if [[ $prevdir == ./ ]]; then
                echo 'No previous directory.' >&2
                return 1
            fi
            cd "$prevdir"
            return
        fi
        if [[ -d $x ]]; then
            prevdir=$x
        fi
    done
    # Should never get here.
    echo 'Directory not changed.' >&2
    return 1
}

next () {
    local foundcwd=
    local cwd=${PWD##*/}
    if [[ -z $cwd ]]; then
        # $PWD must be /
        echo 'No next directory.' >&2
        return 1
    fi
    for x in ../*/; do
        if [[ -n $foundcwd ]]; then
            if [[ -d $x ]]; then
                cd "$x"
                return
            fi
        elif [[ ${x#../} == ${cwd}/ ]]; then
            foundcwd=1
        fi
    done
    echo 'No next directory.' >&2
    return 1
}

¹ไม่สามารถจัดการชื่อไดเรกทอรีที่เป็นไปได้ทั้งหมด แยกlsเอาท์พุทจะไม่ปลอดภัย

² cdอาจไม่จำเป็นต้องมีประสิทธิภาพมากนัก แต่ 6 กระบวนการค่อนข้างมากเกินไป


1
ฉันใช้ zsh จริง ๆ แล้ว แต่ถ้าคุณเปลี่ยนการทดสอบครั้งแรกใน for-loop ของฟังก์ชั่นถัดไป[[ -n $foundcwd ]]คำตอบของคุณจะทำงานภายใต้ bash และ zsh อย่างเท่าเทียมกัน ดีมากและขอขอบคุณที่เขียนสิ่งนี้
Esteis

4

ฟังก์ชั่นต่อไปนี้อนุญาตให้เปลี่ยนเป็นไดเร็กทอรี sibling (ฟังก์ชัน bash)

function sib() {
    ## sib  search sibling directories 
    ##   prompt for choice (when two or more directories are found, current dir is removed from choices) 
    ##   change to directory after selection 
    local substr=$1
    local curdir=$(pwd)
    local choices=$(find .. -maxdepth 1 -type d -name "*${substr}*" | grep -vE '^..$' | sed -e 's:../::' | grep -vE "^${curdir##*/}$" | sort)
    if [ -z "$choices" ]; then
        echo "Sibling directory not found!"
        return
    fi
    local count=$(echo "$choices" | wc -l)
    if [[ $count -eq 1 ]]; then
        cd ../$choices
        return 
    fi
    select dir in $choices; do
        if [ -n "$dir" ]; then
            cd ../$dir
        fi
        break
    done
}

ตัวอย่างการใช้งาน:

$ tree
  .
  ├── component-aaa-01
  ├── component-aaa-02
  ├── component-bbb-01
  ├── component-bbb-02
  ├── component-ccc-01
  ├── component-ccc-02
  └── component-ccc-03
  7 directories, 0 files
  $ cd component-aaa-01/
  $ sib bbb-01
  $ pwd
  component-bbb-01
  $ sib bbb
  $ pwd
  component-bbb-02
  $ sib ccc
  1) component-ccc-01
  2) component-ccc-02
  3) component-ccc-03
  #? 3
  $ pwd
  component-ccc-03
  $ sib 01
  1) component-aaa-01
  2) component-bbb-01
  3) component-ccc-01
  #? 2
  $ pwd
  component-bbb-01

ดีมาก. ฉันรักษาคำตอบของ jw013 ไว้เป็นที่ยอมรับเพราะคำถามและกรณีการใช้ของฉันคือ 'ฉันต้องการย้ายไปยังพี่น้องคนต่อไปและฉันไม่สนใจสิ่งที่เรียกว่า'; แต่สิ่งนี้มีประโยชน์เช่นกันและดีดังนั้นมี upvote
Esteis

2

ผมพบว่าปลายมากกว่าที่commandlinefu.com ฉันจะโพสต์ที่นี่อีกครั้งเพื่อให้สามารถค้นหาได้มากขึ้นโดยมีคำอธิบายและnextคำสั่งเพิ่มเติมขณะที่ฉันอยู่ที่นี่

alias prev='cd ../"$(ls -F .. | grep '/' | grep -B1 -xF "${PWD##*/}/" | head -n 1)"'
alias next='cd ../"$(ls -F .. | grep '/' | grep -A1 -xF "${PWD##*/}/" | tail -n 1)"'

เวทมนตร์อยู่ในบล็อค `$ (... ) มันท่อคำสั่งไม่กี่คำในแต่ละอื่น ๆ ดังนี้:

ls -F .. |   # list items in parent dir; `-F` requests filetype indicators
grep '/' |   # select the directories (written as  `mydir/`)
grep -B1 -xF "${PWD##*/}/" |   # search for the name of the current directory in the output;
                               # print it and the line preceding it
head -n 1    # the first of those two lines contains the name of the previous sibling

2
คำสั่งที่คุณพบมีปัญหาจำนวนหนึ่ง ฉันได้แก้ไขเพื่อแก้ไขสิ่งที่ร้ายแรงที่สุด: มันรักษาชื่อไดเรกทอรีเป็นรูปแบบ grep และอนุญาตให้ตรงกับสตริงย่อย; และมีชื่อไดเรกทอรีที่ได้รับการขยายเชลล์ (การแยกคำและการแบ่งคำ) สองครั้ง (ใช้เครื่องหมายคำพูดคู่ล้อมรอบตัวแปรและคำสั่งแทน: "$foo", "$(foo)") นอกจากนี้การแยกเอาต์พุตของlsไม่น่าเชื่อถือมันอาจล้มเหลวด้วยชื่อไฟล์ที่มีอักขระที่ไม่สามารถพิมพ์ได้
Gilles 'หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.