Làm cách nào để chuyển ký tự đại diện '*' cho tham số đường dẫn của lệnh find thông qua một biến trong tập lệnh?


9

Tôi muốn sử dụng findđể tìm các tệp trong một tập hợp các thư mục bị giới hạn bởi các ký tự đại diện, nhưng ở đó có khoảng trắng trong tên đường dẫn.

Từ dòng lệnh, điều này là dễ dàng. Các ví dụ sau đây đều hoạt động.

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print

Chúng sẽ tìm thấy các tập tin, ví dụ, terminal/my files/moretepid/my files/more.

Tuy nhiên, tôi cần điều này là một phần của kịch bản; những gì tôi cần là một cái gì đó như thế này:

SEARCH='te*/my\ files/more'
find ${SEARCH} -print

Thật không may, bất cứ điều gì tôi làm, tôi dường như không thể trộn các ký tự đại diện và khoảng trắng trong một findlệnh trong một tập lệnh. Ví dụ trên trả về các lỗi sau (lưu ý nhân đôi dấu gạch chéo ngược không mong muốn):

find: te*/my\\’: No such file or directory
find: files/more’: No such file or directory

Cố gắng sử dụng dấu ngoặc kép cũng thất bại.

SEARCH="te*/'my files'/more"
find ${SEARCH} -print

Điều này trả về các lỗi sau, đã bỏ qua ý nghĩa của dấu ngoặc kép:

find: te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory

Đây là một ví dụ nữa.

SEARCH='te*/my files/more'
find ${SEARCH} -print

Như mong đợi:

find: te*/my’: No such file or directory
find: files/more’: No such file or directory

Mỗi biến thể mà tôi đã thử trả về một lỗi.

Tôi có một cách giải quyết, điều này có khả năng nguy hiểm vì nó trả về quá nhiều thư mục. Tôi chuyển đổi tất cả các khoảng trắng thành dấu chấm hỏi (ký tự đại diện một ký tự) như thế này:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

Điều này tương đương với:

find te*/my?files/more -print

Điều này trả về không chỉ các thư mục chính xác mà còn terse/myxfiles/more, nó không được phép.

Làm thế nào tôi có thể đạt được những gì tôi đang cố gắng làm? Google đã không giúp tôi :(


@KasiyA Tôi đang sử dụng bash; bạn phải sử dụng cái gì đó khác, vì tôi chưa từng thấy công trình đó trước đây. Lệnh kết quả SEARCH: command not foundvới lệnh find -printđược thực thi.
Paddy Landau

Một shot trong bóng tối, nhưng những gì về trích dẫn? find "${SEARCH}" -print?
Alaa Ali

@AlaaAli Không, nó không hoạt động, vì trích dẫn ngăn Bash sử dụng các ký tự đại diện. Nó sẽ tìm một đường dẫn cụ thể với tên (trong ví dụ của tôi) te*/'my files'/more.
Paddy Landau

Câu trả lời:


8

Lệnh chính xác tương tự sẽ hoạt động tốt trong một tập lệnh:

#!/usr/bin/env bash
find  te*/my\ files/ -print

Nếu bạn cần phải có nó như một biến, nó sẽ phức tạp hơn một chút:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

CẢNH BÁO:

Sử dụng evalnhư thế là không an toàn và có thể dẫn đến việc thực thi mã tùy ý và có thể có hại nếu tên tệp của bạn có thể chứa một số ký tự nhất định. Xem bash FAQ 48 để biết chi tiết.

Tốt hơn là vượt qua con đường như một đối số:

#!/usr/bin/env bash
find "$@" -name "file*"

Một cách tiếp cận khác là tránh findhoàn toàn và sử dụng các tính năng và khối lượng toàn cầu mở rộng của bash:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

Các globstartùy chọn bash cho phép bạn sử dụng **để phù hợp với đệ quy:

globstar
      If set, the pattern ** used in a pathname expansion con
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.

Để làm cho nó hoạt động 100% như tìm và bao gồm dotfiles (tệp ẩn), hãy sử dụng

#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done

Bạn thậm chí có thể echotrực tiếp chúng mà không cần vòng lặp:

echo te*/my\ files/**

2
Tôi đã đặt đây là câu trả lời vì những bình luận hữu ích mà terdon đã đưa ra (không quên những bình luận hữu ích từ người khác). Tôi đã sử dụng khả năng toàn cầu hóa của Bash trên dòng lệnh để truyền nhiều đường dẫn tới tập lệnh của mình, thay vì tập lệnh cố gắng sắp xếp nó. Nó hoạt động tốt.
Paddy Landau

2

Làm thế nào về mảng?

$ tree Desktop/ Documents/
Desktop/
└── my folder
    └── more
        └── file
Documents/
└── my folder
    ├── folder
    └── more

5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}" 
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder

(*)mở rộng thành một mảng của bất cứ thứ gì khớp với ký tự đại diện. Và "${SEARCH[@]}"mở rộng thành tất cả các phần tử trong mảng ( [@]), với mỗi phần được trích dẫn riêng lẻ.

Được tin, tôi nhận ra rằng bản thân nên có khả năng này. Cái gì đó như:

find . -path 'D*/my folder/more/'

Ý tưởng thông minh, nhưng than ôi, nó không hoạt động. Tại sao? Bởi vì chính đường dẫn được giữ trong một biến; do đó, INPUTPATH='te*/my files/moreSEARCH=(${INPUTPATH}). Cho dù tôi thay đổi cách tôi làm điều này như thế nào, tôi vẫn kết thúc với một kết quả không có chức năng. Điều này dường như là không thể!
Paddy Landau

Tất nhiên điều này là đúng, nhưng OP cần phải thực hiện nó trong một kịch bản. Điều đó thay đổi mọi thứ vì việc mở rộng các ký tự đại diện trở nên phức tạp hơn đáng kể và điều này không hoạt động.
terdon

@PaddyLandau Trong trường hợp này, tại sao có thể giúp bạn không sử dụng findcủa -pathbộ lọc? Nó sử dụng các ký tự đại diện và chắc chắn không cần mở rộng.
muru

@muru Điều đó thật thú vị; Tôi không biết về -path. Tuy nhiên, trong 10 phút qua, tôi đã tìm ra câu trả lời: sử dụng eval! Nó có vẻ đơn giản hơn -path.
Paddy Landau

2
@terdon và muru và mọi người: Cảm ơn bạn. Tôi đã nghe những gì bạn đã nói, và tôi nhận ra rằng tôi nên làm cho kịch bản của mình làm một việc, và để Bash globalbing chuyển nhiều tệp hoặc đường dẫn đến tập lệnh. Tôi đã sửa đổi kịch bản của tôi như vậy. Nó hoạt động tốt và phù hợp hơn với triết lý Linux. Cám ơn bạn một lần nữa!
Paddy Landau

0

Cuối cùng tôi đã tìm ra câu trả lời.

Thêm dấu gạch chéo ngược vào tất cả các khoảng trắng:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }

Tại thời điểm này, SEARCHchứa te*/my\ files/more.

Sau đó, sử dụng eval.

eval find ${SEARCH} -print

Nó đơn giản mà! Sử dụng evalbỏ qua việc giải thích đó ${SEARCH}là từ một biến.



@terdon Cảm ơn bạn đã cảnh báo. Trở lại với bản vẽ!
Paddy Landau

Vâng, điều này là đáng ngạc nhiên khó khăn. Tôi vừa cập nhật câu trả lời của mình với một cách tiếp cận khác, tại sao không sử dụng Globing thay thế? Nếu điều đó vẫn không hiệu quả, tôi khuyên bạn nên đăng một câu hỏi mới trên Unix & Linux để giải thích mục tiêu cuối cùng của bạn là gì và tại sao bạn cần phải có mô hình như một biến. Loại điều này là thích hơn để có được một câu trả lời tốt hơn ở đó.
terdon
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.