Liệt kê tất cả các thư mục KHÔNG có tệp có tên tệp đã cho bên trong


11

Làm thế nào tôi có thể liệt kê tất cả các thư mục không có tệp có tên tệp đã cho bên trong? ví dụ như cây này

/
  /a
     README
     file001
     file002
  /b
     README
     file001
  /c
     file003

Tôi muốn liệt kê các thư mục không có tệp có tên README, trong trường hợp này nó sẽ là thư mục /c. Làm thế nào tôi có thể làm điều đó? Tôi không thể nghĩ ra bất kỳ cú pháp nào bằng cách sử dụng find.


Thật xấu hổ, thật xấu hổ khi bạn thậm chí không tìm kiếm: Askubfox.com/questions/196960/ cấp
slm

Tôi đã không nghĩ về các từ khóa đúng khi tìm kiếm, có lẽ.
Renan

2
Tôi chỉ bán thân thôi. Tôi đã ở đó nhiều lần mà tôi không thể nghĩ ra từ đúng để tìm kiếm thứ gì đó 8-).
slm

Câu trả lời:


5

Giả sử một findtriển khai như GNU findchấp nhận {}được nhúng trong một đối số để -exec:

$ find . -type d \! -exec test -e '{}/README' \; -print

Thí dụ

Ở đây các thư mục 1/1 đến 5/5 có README, các thư mục khác đều trống.

$ tree 
.
|-- 1
|   `-- 1
|       `-- README
|-- 10
|   `-- 10
|-- 2
|   `-- 2
|       `-- README
|-- 3
|   `-- 3
|       `-- README
|-- 4
|   `-- 4
|       `-- README
|-- 5
|   `-- 5
|       `-- README
|-- 6
|   `-- 6
|-- 7
|   `-- 7
|-- 8
|   `-- 8
`-- 9
    `-- 9

Bây giờ khi chúng tôi chạy phiên bản findlệnh này:

$ find . -type d \! -exec test -e '{}/README' \; -print
.
./10
./10/10
./7
./7/7
./9
./9/9
./6
./6/6
./5
./8
./8/8
./4
./1
./3
./2

Người giới thiệu


Cách sửa đổi lệnh để tìm kiếm các thư mục con không có phần mở rộng tệp cụ thể (giả sử * .txt). Sửa đổi README bằng * .txt dường như không hoạt động
WanderingMind

@WanderingMind - nếu bạn có câu hỏi mới, vui lòng hỏi nó như một câu hỏi mới trên trang web ;-)
slm

3

Bạn có thể sử dụng -exectùy chọn findđể kiểm tra tệp và sau đó in tất cả các kết quả mà kiểm tra không thành công.

find /path/to/base -mindepth 1 -maxdepth 1 -type d -exec test -e {}/README \; -o -print

3

Không cần find. Chỉ cần sử dụng vỏ:

for d in */; do [ -f "$d"README ] || printf '%s\n' "$d"; done
c/

Nếu bạn cần nó được đệ quy, bạn có thể sử dụng (cho bash, zshcó thể làm điều này theo mặc định, sử dụng set -o globstartrong ksh93):

shopt -s globstar
for d in **/; do [ -f "$d"README ] || printf '%s\n' "$d"; done

(lưu ý rằng các tệp chấm được loại trừ theo mặc định).


2

Với zshvòng loại toàn cầu ( echuỗi ):

print -rl -- *(/e_'[[ ! -f $REPLY/README ]]'_)

hoặc là

print -rl -- *(/^e_'[[ -f $REPLY/README ]]'_)

thêm Dđể bao gồm các thư mục ẩn:

print -rl -- *(D/e_'[[ ! -f $REPLY/README ]]'_)

/chỉ chọn các thư mục và e_'[[ ! -f $REPLY/README ]]'_chỉ chọn các tên thư mục mà mã shell giữa các trích dẫn trả về true, đó là cho mỗi tên thư mục ( $REPLY) mà toàn cầu *(/)mở rộng, nó chạy [[ ! -f $REPLY/README ]]và giữ tên của thư mục nếu kết quả là true.
Biểu mẫu thứ hai ^e_'.....'_sử dụng cùng một vòng loại toàn cầu, bị phủ định (nhưng lần này biểu thức điều kiện không bị phủ định [[ -f $REPLY/README ]]:).


Ở trên sẽ chỉ trả lại tên thư mục trong thư mục hiện tại.
Nếu bạn muốn tìm kiếm đệ quy (một lần nữa, để bao gồm các thư mục ẩn, hãy thêm Dvòng loại):

print -rl ./**/*(/e_'[[ ! -f $REPLY/README ]]'_)

2

Có thể, bạn có thể làm:

find . -type d -exec sh -c '
  for dir do
    [ -f "$dir/README" ] || printf "%s\n" "$dir"
  done' sh '{}' +

[ -f file ]kiểm tra nếu tệp tồn tại được xác nhận là tệp thông thường (sau khi phân giải symlink).

Nếu bạn muốn kiểm tra rằng nó chỉ tồn tại (như một mục trong thư mục đó), bất kể loại nào, bạn cần [ -e file ] || [ -L file ]:, mặc dù lưu ý rằng bạn cần có quyền tìm kiếm vào thư mục để thực hiện các kiểm tra đó. Bạn có thể muốn thêm một số [ -x "$dir" ]bài kiểm tra vào tài khoản cho những trường hợp như:

find . -type d -exec sh -c '
  for dir do
    if [ -x "$dir" ]; then
      [ -f "$dir/README" ] || printf "%s\n" "$dir"
    else
      printf >&2 "Cannot tell for \"%s\"\n" "$dir"
    fi
  done' sh '{}' +

Hoặc để tránh điều kiện cuộc đua, với zsh:

find . -type d -exec zsh -c '
  zmodload zsh/system
  for dir do
    ERRNO=0
    if [ ! -f "$dir/README" ]; then
      if [ "$errnos[ERRNO]" = ENOENT ]; then
        printf "%s\n" "$dir"
      else
        syserror -p "ERROR: $dir/README: "
      fi
    fi
  done' zsh '{}' +

Xem thêm Làm thế nào để tôi biết nếu một tệp thông thường không tồn tại trong Bash? trên SO.

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.