Vô hiệu hóa hoàn thành tab trong Bash cho một số thư mục có chứa một số lượng lớn các tệp?


19

Tôi đang làm việc với số lượng lớn các tệp mà tôi giữ trong một thư mục. Mỗi lần tôi vào thư mục đó và vô tình nhấn Tabhai lần, sẽ mất quá nhiều thời gian (có thể hơn một phút) để hiển thị các tệp khớp với mẫu và tôi khá khó chịu với hành vi này.

Ví dụ, cấu trúc thư mục của tôi là:

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

Vì tôi vẫn thích hoàn thành, có cách nào để tắt tính năng này chỉ trong data/thư mục không? Giống như đặt thời gian chờ trong 5 giây hoặc tập lệnh tự động tắt hoàn thành khi trong thư mục cụ thể?


Bạn sẽ chuyển sang zsh hơn này? (Tôi không biết nếu có thể trong bash. Tôi biết điều đó có thể xảy ra trong zsh, nhưng nó có thể không dễ dàng, tôi đã không kiểm tra.)
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


9

Điều này không hoàn hảo, nhưng sau đó một lần nữa hoàn thành bash là một điều khá khó khăn ...

Cách đơn giản nhất là trên cơ sở mỗi lệnh, nó linh hoạt hơn một chút so với FIGNORE, bạn có thể làm:

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

Điều này hướng dẫn tự động hoàn thành mà hoàn thành cho vi cho các tệp và để loại bỏ các mẫu phù hợp với -Xbộ lọc. Nhược điểm là mẫu không được chuẩn hóa, do đó ../datavà các biến thể sẽ không khớp.

Điều tốt nhất tiếp theo có thể là một PROMPT_COMMANDchức năng tùy chỉnh :

# 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

Điều này vô hiệu hóa hoàn thành (hoàn toàn) khi bạn đang ở trong thư mục, nhưng nó vô hiệu hóa nó cho mọi đường dẫn không chỉ các tệp trong thư mục đó.

Nhìn chung sẽ hữu ích hơn khi vô hiệu hóa một cách chọn lọc điều này cho các đường dẫn đã xác định, nhưng tôi tin rằng cách duy nhất là sử dụng chức năng hoàn thành mặc định (bash-4.1 trở lên với complete -D) và rất nhiều điều sai lầm.

Điều này sẽ làm việc cho bạn, nhưng nó có thể có tác dụng phụ ngoài ý muốn (nghĩa là thay đổi hoàn thành dự kiến ​​trong một số trường hợp):

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

Điều này hoạt động để hoàn thành vi, các lệnh khác có thể được thêm vào khi cần thiết. Nó sẽ dừng hoàn thành cho các tập tin trong các thư mục được đặt tên bất kể đường dẫn hoặc thư mục làm việc.

Tôi tin rằng cách tiếp cận chung complete -Dlà tự động thêm các hàm hoàn thành cho mỗi lệnh khi nó gặp phải. Người ta cũng có thể cần thêm complete -E(hoàn thành tên lệnh khi bộ đệm đầu vào trống).


Cập nhật Đây là phiên bản kết hợp của các PROMPT_COMMANDgiải pháp chức năng và hoàn thành, tôi nghĩ dễ hiểu hơn và hack:

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

Hàm nhắc này đặt nocompletebiến khi bạn nhập một trong các thư mục được cấu hình. Hành vi hoàn thành đã sửa đổi chỉ khởi động khi biến đó không trống chỉ khi bạn cố gắng hoàn thành từ một chuỗi trống, do đó cho phép hoàn thành tên một phần (loại bỏ -z "$cur"điều kiện để ngăn chặn hoàn thành). Nhận xét hai printfdòng cho hoạt động im lặng.

Các tùy chọn khác bao gồm .noautocompletetệp cờ trên mỗi thư mục mà bạn có thể có touchtrong một thư mục khi cần; và đoán kích thước thư mục bằng GNU stat. Bạn có thể sử dụng bất kỳ hoặc tất cả ba tùy chọn đó.

( statPhương pháp chỉ là phỏng đoán , kích thước thư mục được báo cáo tăng theo nội dung của nó, đó là "dấu nước cao" thường không co lại khi các tệp bị xóa mà không có sự can thiệp của chính quyền. Nó rẻ hơn so với việc xác định nội dung thực sự của một tiềm năng lớn thư mục. Hành vi chính xác và gia tăng trên mỗi tệp phụ thuộc vào hệ thống tệp cơ bản. Tôi thấy đó là một chỉ báo đáng tin cậy trên các hệ thống linux ext2 / 3/4.)

bash thêm một khoảng trắng ngay cả khi trả về hoàn thành trống (điều này chỉ xảy ra khi hoàn thành ở cuối dòng). Bạn có thể thêm -o nospacevàocomplete lệnh để ngăn chặn điều này.

Một điều khó khăn còn lại là nếu bạn sao lưu con trỏ vào đầu mã thông báo và nhấn tab, việc hoàn thành mặc định sẽ khởi động lại. Hãy coi đó là một tính năng ;-)

(Hoặc bạn có thể futz xung quanh với ${COMP_LINE:$COMP_POINT-1:1}nếu bạn thích quá kỹ thuật, nhưng tôi tìm bash bản thân thất bại trong việc thiết lập các biến hoàn đáng tin cậy khi bạn sao lưu và cố gắng hoàn thành vào giữa một lệnh.)


6

Nếu datathư mục của bạn chứa các tệp có hậu tố cụ thể, ví dụ: .out thì bạn có thể đặt bashbiến của mình FIGNOREthành ".out"và các biến này sẽ bị bỏ qua. Mặc dù cũng có thể sử dụng tên thư mục, nhưng điều này không giúp ích cho tên thư mục làm việc hiện tại.

Thí dụ:

Tạo hàng ngàn tệp thử nghiệm 100KB trên máy chủ vật lý với đột kích 1 ổ cứng:

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

Đặt bashbiến:
$ FIGNORE=".json"

Tạo tệp thử nghiệm:
$ touch test.out

Kiểm tra tại 5.000 tệp:

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

Không có độ trễ giữa vi và đơn tabtrước khi test.outxuất hiện.

Kiểm tra tại 50.000 tệp:

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

Tab đơn tạo ra một phần của độ trễ thứ hai trước khi test.outxuất hiện.

Kiểm tra tại 200.000 tệp:

$ 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

Độ trễ 1 giây giữa vi và đơn tabtrước khi test.outxuất hiện.

Tài liệu tham khảo:

Từ người đàn ông bash

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
sẽ không giúp đỡ, hãy thử tạo tập tin dir với khoảng ~ 100k tập tin. sau đó tôi làm $ vi <tab><tab>, nó sẽ mất rất nhiều thời gian : \
neizod

vẫn không giải quyết được vấn đề, giả sử tôi có 100k .jsontệp và một tệp văn bản có tên foo.txt. bật FIGNORE='.json'lên, tôi gõ $ vi <tab>và vẫn cần đợi nửa phút để nó hoàn thành $ vi foo.txt. --- kịch bản thực Tôi sẽ không chỉnh sửa hoặc trộn các loại tệp bên trong thư mục đó, nhưng nếu tôi bật FIGNOREvà (vô tình) nhấn tab, bàn phím của tôi sẽ bị đóng băng trong một thời gian dài mà không có gợi ý nào giống như Display all 188275 possibilities? (y or n), điều đó cho tôi biết rằng tôi có thể lấy lại bàn phím của mình.
neizod

@neizod - Xin lỗi, tôi đã thử nhiều kịch bản để FIGNORE hoạt động ở cấp độ thư mục, nhưng không có kết quả. Tôi đoán một giải pháp tùy chỉnh là bắt buộc, hoặc bạn chỉ cần vô hiệu hóa hoàn thành bash trên máy chủ đó hoặc thử giải pháp zsh chi tiết trên trang web chị em của chúng tôi mà Gilles đã đề cập.
geedoubleya

Xin lỗi, nó chỉ có 1 giây trên hệ thống của bạn, khoảng nửa phút trên hệ thống của tôi. Vì vậy, tôi đoán tôi sẽ đi mua một PC mới để giải pháp này hoạt động.
neizod

0

Đoán đây là những gì bạn muốn (mượn một số mã từ @ mr.spuratic)

Lưu ý điều này không ngăn bạn nhấn TAB bằng đường dẫn đầy đủ (ví dụ: 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
}  

Đừng quên về pushd/ popd. Vì điều này chỉ sử dụng một mảng thông thường, không phải là một mảng kết hợp, nó cũng sẽ hoạt động trong bash 3.x.
mr.spuratic
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.