ปิดใช้งานการเติมแท็บใน Bash สำหรับบางไดเรกทอรีที่มีไฟล์จำนวนมากหรือไม่


19

ฉันทำงานกับไฟล์จำนวนมากซึ่งฉันเก็บไว้ในไดเรกทอรี ทุกครั้งที่ฉันเข้าไปยังไดเรกทอรีนั้นและกดTabสองครั้งโดยไม่ได้ตั้งใจอาจใช้เวลานานเกินไป (อาจใช้เวลานานกว่าหนึ่งนาที) ในการแสดงไฟล์ที่ตรงกับรูปแบบและฉันก็ค่อนข้างรำคาญกับพฤติกรรมนี้

ตัวอย่างเช่นโครงสร้างไดเรกทอรีของฉันคือ:

my-project/
├── docs/
├── data/        <---- contains around 200k files.
└── analyser/

เนื่องจากฉันยังรักความสมบูรณ์อยู่มีการปิดใช้งานคุณลักษณะนี้เฉพาะในdata/ไดเรกทอรีหรือไม่ เช่นเดียวกับการหมดเวลาที่กำหนดไว้เป็นเวลา 5 วินาทีหรือสคริปต์ที่ปิดอัตโนมัติเมื่ออยู่ในไดเรกทอรีเฉพาะ


คุณจะเปลี่ยนไปใช้ zsh มากกว่านี้ไหม (ฉันไม่ทราบว่าเป็นไปได้ในการทุบตีฉันรู้ว่ามันเป็นไปได้ใน zsh แต่มันอาจจะไม่ง่ายฉันยังไม่ได้ตรวจสอบ)
Gilles 'ดังนั้นหยุดความชั่วร้าย'

คำตอบ:


9

มันไม่สมบูรณ์แบบ แต่การทุบตีเสร็จก็เป็นเรื่องที่ค่อนข้างยุ่งยาก ...

วิธีที่ง่ายที่สุดอยู่บนพื้นฐานของคำสั่งต่อมันยืดหยุ่นกว่าเล็กน้อยFIGNOREคุณสามารถทำ:

 complete -f -X "/myproject/data/*" vi

สิ่งนี้แนะนำให้เติมข้อความอัตโนมัติให้เสร็จสมบูรณ์สำหรับviไฟล์และเพื่อลบรูปแบบที่ตรงกับ-Xตัวกรอง ข้อเสียคือรูปแบบนั้นไม่ได้เป็นมาตรฐานดังนั้น../dataรูปแบบจะไม่ตรงกัน

สิ่งที่ดีที่สุดถัดไปอาจเป็นPROMPT_COMMANDฟังก์ชั่นที่กำหนดเอง:

# associative arrays of non-autocomplete directories
declare -A noacdirs=([/myproject/data]=1 )

function _myprompt {
    [[ -n "${noacdirs[$PWD]}" ]] && {
       echo autocomplete off
       bind 'set disable-completion on'
    } || {
       echo autocomplete on
       bind 'set disable-completion off'
    }
} 

PROMPT_COMMAND=_myprompt

สิ่งนี้จะปิดใช้งานการทำให้สมบูรณ์ (สมบูรณ์) เมื่อคุณอยู่ในไดเรกทอรีแต่จะปิดการใช้งานสำหรับทุกเส้นทางไม่ใช่แค่ไฟล์ในไดเรกทอรีนั้น

มันจะมีประโยชน์มากกว่าโดยทั่วไปในการเลือกปิดการใช้งานนี้สำหรับเส้นทางที่กำหนด แต่ฉันเชื่อว่าวิธีเดียวคือการใช้ฟังก์ชั่นเริ่มต้นที่สมบูรณ์ (bash-4.1 และใหม่กว่าด้วยcomplete -D) และยุ่งมาก

สิ่งนี้ควรใช้กับคุณ แต่อาจมีผลข้างเคียงที่ไม่ตั้งใจ (เช่นการเปลี่ยนแปลงความสมบูรณ์ที่คาดไว้ในบางกรณี):

declare -A noacdirs=([/myproject/data]=1 )

_xcomplete() {
    local cur=${COMP_WORDS[COMP_CWORD]} # the current token

    name=$(readlink -f "${cur:-./}")  # poor man's path canonify
    dirname=$(dirname "$name/.")

    [[ -n "${noacdirs[$dirname]}" ]] && {
        COMPREPLY=( "" )   # dummy to prevent completion
        return
    }
    # let default kick in
    COMPREPLY=()
}

complete -o bashdefault -o default -F _xcomplete vi

การทำงานนี้เสร็จสมบูรณ์viสามารถเพิ่มคำสั่งอื่นได้ตามต้องการ ควรหยุดการทำให้ไฟล์ในไดเรกทอรีที่มีชื่อเสร็จสมบูรณ์โดยไม่คำนึงถึงพา ธ หรือไดเร็กทอรีการทำงาน

ฉันเชื่อว่าวิธีการทั่วไปด้วยcomplete -Dคือการเพิ่มฟังก์ชั่นความสมบูรณ์สำหรับแต่ละคำสั่งตามที่พบ หนึ่งอาจต้องเพิ่มcomplete -E(ชื่อสมบูรณ์ของคำสั่งเมื่อบัฟเฟอร์อินพุตว่างเปล่า)


อัปเดต ต่อไปนี้เป็นรุ่นไฮบริดของPROMPT_COMMANDโซลูชันฟังก์ชั่นและเสร็จสมบูรณ์มันง่ายกว่าที่จะเข้าใจและแฮ็คฉันคิดว่า:

declare -A noacdirs=([/myproject/data]=1 [/project2/bigdata]=1)

_xcomplete() {
    local cmd=${COMP_WORDS[0]}
    local cur=${COMP_WORDS[COMP_CWORD]} # the current token

    [[ -z "$cur" && -n "$nocomplete" ]] && {
        printf "\n(restricted completion for $cmd in $nocomplete)\n"  
        printf "$PS2 $COMP_LINE"
        COMPREPLY=( "" )   # dummy to prevent completion
        return
    }
    COMPREPLY=()       # let default kick in
}

function _myprompt {
    nocomplete=
    # uncomment next line for hard-coded list of directories
    [[ -n "${noacdirs[$PWD]}" ]] && nocomplete=$PWD
    # uncomment next line for per-directory ".noautocomplete"
    # [[ -f ./.noautocomplete ]] && nocomplete=$PWD
    # uncomment next line for size-based guessing of large directories
    # [[ $(stat -c %s .) -gt 512*1024 ]] && nocomplete=$PWD
} 

PROMPT_COMMAND=_myprompt
complete -o bashdefault -o default -F _xcomplete vi cp scp diff

ฟังก์ชันพรอมต์นี้ตั้งค่าnocompleteตัวแปรเมื่อคุณป้อนหนึ่งในไดเรกทอรีที่กำหนดค่า พฤติกรรมการทำให้เสร็จสมบูรณ์ที่ปรับเปลี่ยนจะเริ่มทำงานเฉพาะในกรณีที่ตัวแปรนั้นไม่ว่างเปล่าและเมื่อคุณลองดำเนินการให้เสร็จสิ้นจากสตริงว่างเท่านั้นดังนั้นจึงอนุญาตให้ใช้ชื่อบางส่วนได้-z "$cur"เสร็จสมบูรณ์ ใส่เครื่องหมายสองprintfบรรทัดเพื่อการทำงานที่เงียบ

ตัวเลือกอื่นรวมถึง.noautocompleteไฟล์การตั้งค่าสถานะสำหรับแต่ละไดเรกทอรีที่คุณสามารถทำได้touchในไดเรกทอรีตามต้องการ และคาดเดาขนาดไดเรกทอรีใช้ statGNU คุณสามารถใช้ตัวเลือกใดก็ได้หรือทั้งสามอย่าง

( statเมธอดนี้เป็นเพียงการคาดเดาขนาดไดเรกตอรีที่รายงานนั้นเติบโตขึ้นพร้อมกับเนื้อหามันคือ "high water mark" ซึ่งโดยปกติแล้วจะไม่ย่อขนาดเมื่อไฟล์ถูกลบโดยไม่ต้องมีการดูแลระบบ ไดเรกทอรีพฤติกรรมที่แม่นยำและการเพิ่มขึ้นต่อไฟล์ขึ้นอยู่กับระบบไฟล์พื้นฐานฉันพบว่ามันเป็นตัวบ่งชี้ที่เชื่อถือได้บนระบบ linux ext2 / 3/4 อย่างน้อย)

bash เพิ่มช่องว่างพิเศษแม้ว่าจะส่งคืนความสมบูรณ์ที่ว่างเปล่า (สิ่งนี้จะเกิดขึ้นเฉพาะในตอนท้ายของบรรทัด) คุณสามารถเพิ่ม-o nospaceไปยังcompleteคำสั่งเพื่อป้องกันปัญหานี้

niggle ที่เหลืออยู่อย่างหนึ่งคือถ้าคุณสำรองเคอร์เซอร์ไปที่จุดเริ่มต้นของแท็บโทเค็นและ Hit การเริ่มต้นที่สมบูรณ์จะเริ่มขึ้นอีกครั้ง พิจารณาคุณลักษณะ ;-)

(หรือคุณสามารถ futz รอบด้วย${COMP_LINE:$COMP_POINT-1:1}ถ้าคุณชอบ over-engineering แต่ฉันพบว่าตัวเองทุบตีล้มเหลวในการตั้งค่าตัวแปรเสร็จสมบูรณ์ได้อย่างน่าเชื่อถือเมื่อคุณสำรองข้อมูลและพยายามดำเนินการเสร็จสิ้นในกลางของคำสั่ง)


6

หากdataไดเรกทอรีของคุณมีไฟล์ที่มีคำต่อท้ายที่เฉพาะเจาะจงเช่น .outจากนั้นคุณสามารถตั้งค่าbashตัวแปรของคุณFIGNOREเป็น".out"และสิ่งเหล่านี้จะถูกละเว้น แม้ว่ามันจะเป็นไปได้ที่จะใช้ชื่อไดเรกทอรีเช่นกัน แต่ก็ไม่ได้ช่วยในชื่อไดเรกทอรีการทำงานปัจจุบัน

ตัวอย่าง:

สร้างไฟล์ทดสอบ 100KB หลายพันไฟล์บนโฮสต์จริงด้วย RAID 1 HDD:

for i in {1..200000}; do dd if=/dev/urandom bs=102400 count=1 of=file$i.json; done

ตั้งค่าbashตัวแปร:
$ FIGNORE=".json"

สร้างไฟล์ทดสอบ:
$ touch test.out

ทดสอบที่5,000ไฟล์:

$ ls *.json|wc -l
5587
$ vi test.out

ไม่ล่าช้าระหว่าง vi และซิงเกิ้ลtabก่อนที่จะtest.outปรากฏขึ้น

ทดสอบที่50,000ไฟล์:

$ ls *.json|wc -l
51854
$ vi test.out

แท็บเดียวจะสร้างส่วนหนึ่งของความล่าช้าครั้งที่สองก่อนที่จะtest.outปรากฏขึ้น

ทดสอบที่200,000ไฟล์:

$ ls *.json|wc -l
bash: /bin/ls: Argument list too long
$ ls | awk 'BEGIN {count=0} /.*.json/ {count++} END {print count}'
200000
$ vi test.out

การหน่วงเวลา 1 วินาทีระหว่าง vi และซิงเกิลtabก่อนจะtest.outปรากฏขึ้น

อ้างอิง:

ตัดตอนมาจากการทุบตีมนุษย์

FIGNORE
              A  colon-separated  list  of  suffixes to ignore when performing filename completion (see READLINE below).  A filename whose suffix matches one of the entries in FIGNORE is
              excluded from the list of matched filenames.  A sample value is ".o:~".

1
จะไม่ช่วยลองสร้าง dir w / ประมาณ ~ 100k ไฟล์ จากนั้นฉัน$ vi <tab><tab>ก็ใช้เวลานานมาก : \
neizod

ยังคงไม่สามารถแก้ปัญหาได้สมมติว่าฉันมี.jsonไฟล์100k และมีไฟล์ข้อความชื่อfoo.txtเดียว เปลี่ยน FIGNORE='.json'ผมพิมพ์และยังคงต้องรอครึ่งนาทีจึงเสร็จฉันไป$ vi <tab> $ vi foo.txt--- สถานการณ์จริงฉันจะไม่แก้ไขหรือผสมประเภทไฟล์ใน dir นั้น แต่ถ้าฉันเปิดFIGNOREและ (โดยไม่ตั้งใจ) แท็บกดแป้นพิมพ์ของฉันจะค้างเป็นเวลานานโดยไม่พูดอะไรทำนองDisplay all 188275 possibilities? (y or n)นี้ซึ่งบอกฉัน ฉันสามารถเอาคืนคีย์บอร์ดของฉันได้
neizod

@neizod - ขออภัยฉันลองหลาย ๆ สถานการณ์เพื่อให้ FIGNORE ทำงานในระดับไดเรกทอรีเพื่อประโยชน์ ฉันเดาว่าต้องใช้โซลูชันที่กำหนดเองหรือคุณเพียงแค่ปิดการใช้งาน bash complete บนโฮสต์นั้นหรือลองใช้โซลูชั่น zsh ที่มีรายละเอียดในเว็บไซต์น้องสาวของเราที่ Gilles พูดถึง
geedoubleya

ขออภัยระบบของคุณใช้เวลาเพียง 1 วินาทีระบบของฉันใช้เวลาประมาณครึ่งนาที ดังนั้นฉันเดาว่าฉันจะไปซื้อพีซีเครื่องใหม่เพื่อให้โซลูชันนี้ใช้งานได้
neizod

0

คิดว่านี่คือสิ่งที่คุณต้องการ (ยืมรหัสจาก @ mr.spuratic)

หมายเหตุสิ่งนี้ไม่ได้ป้องกันคุณจากการกด TAB โดยใช้เส้นทางแบบเต็ม (เช่น vim /directory/contains/lots/files<tab><tab>

function cd() {
  builtin cd "$@"
  local NOCOMPLETION=( "/" "/lib" )
  local IS_NOCOMPLETION=0
  for dir in "${NOCOMPLETION[@]}"
  do
    echo $dir
    if [[ $(pwd) == "$dir" ]]
    then
      echo "disable completion"
      bind "set disable-completion on"
      IS_NOCOMPLETION=1
      return
    fi                                                                                                                                                                                              
  done
  if [[ $IS_NOCOMPLETION -eq 0 ]]
  then
    echo "enable completion"
    bind "set disable-completion off"
  fi
}  

อย่าลืมเกี่ยวกับ/pushd popdเนื่องจากสิ่งนี้ใช้เฉพาะอาเรย์ปกติไม่ใช่อาเรย์แบบเชื่อมโยงมันจึงสามารถใช้งานใน bash 3.x ได้เช่นกัน
mr.spuratic
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.