Làm thế nào để lặp qua các thư mục trong Linux?


168

Tôi đang viết một tập lệnh bash trên Linux và cần phải đi qua tất cả các tên thư mục con trong một thư mục nhất định. Làm thế nào tôi có thể lặp qua các thư mục này (và bỏ qua các tệp thông thường)?

Ví dụ:
thư mục đã cho là /tmp/
nó có các thư mục con sau:/tmp/A, /tmp/B, /tmp/C

Tôi muốn lấy A, B, C.


Câu trả lời:


129
cd /tmp
find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n'

Một lời giải thích ngắn gọn:

  • find tìm tập tin (khá rõ ràng)

  • .là thư mục hiện tại, sau thư mục cdnày /tmp(IMHO này linh hoạt hơn so với việc có /tmptrực tiếp trong findlệnh. Bạn chỉ có một nơi cd, để thay đổi, nếu bạn muốn có nhiều hành động diễn ra trong thư mục này)

  • -maxdepth 1-mindepth 1đảm bảo rằng findchỉ nhìn trong thư mục hiện tại và không bao gồm .chính nó trong kết quả

  • -type d chỉ tìm thư mục

  • -printf '%f\n chỉ in tên thư mục tìm thấy (cộng với một dòng mới) cho mỗi lần nhấn.

Et voilà!


Nhân tiện: Lưu ý rằng tất cả các câu trả lời cũng sẽ tìm thấy các thư mục ẩn (nghĩa là các thư mục bắt đầu bằng dấu chấm)! Nếu bạn không muốn điều này, hãy thêm `-regex '\ ./ [^ \.]. *'` Trước các tùy chọn printf hoặc exec.
Boldewyn

1
Hữu ích - nếu bạn chỉ cần thực hiện một lệnh cho mỗi lần truy cập ;-) - Chỉ tôi có một số lệnh để thực thi :-(.
Martin

Không có vấn đề gì: Thích ứng giải pháp này: transnum.blogspot.de/2008/11/ trên Bên trong while..donevòng lặp bạn có thể phát điên.
Boldewyn

1
Đây là một phương pháp rất hay cho một số trường hợp sử dụng; find's -exectùy chọn cho phép bạn chạy bất kỳ lệnh cho mỗi tập tin / thư mục.
jtpereyda

451

Tất cả các câu trả lời cho đến nay sử dụng find, vì vậy đây là một câu trả lời chỉ với vỏ. Không cần các công cụ bên ngoài trong trường hợp của bạn:

for dir in /tmp/*/     # list directories in the form "/tmp/dirname/"
do
    dir=${dir%*/}      # remove the trailing "/"
    echo ${dir##*/}    # print everything after the final "/"
done

18
+1 - Tại sao phải bận tâm findkhi bạn có thể thêm dấu gạch chéo vào ký tự đại diện
Tobias Kienzler

19
như vậy for dir in */; do echo $dir; donelà cho các thư mục trong thư mục hiện tại.
Ehtesh Choudhury

41
Sẽ tốt hơn nếu nó có thể chứa một lời giải thích những gì dir=${dir%*/}echo ${dir##*/}đang làm.
Jeremy S.

11
Đó là bởi vì dir biến sẽ bao gồm dấu gạch chéo ngược ở cuối. Ông chỉ cần loại bỏ nó để có một tên sạch. % sẽ xóa / ở cuối. ## sẽ xóa / ở đầu. Đây là các toán tử để xử lý các chuỗi con.
sfratini

8
Nếu không có kết quả khớp, vòng lặp sẽ được kích hoạt /tmp/*/, sẽ là khôn ngoan nếu bao gồm một kiểm tra để xem liệu thư mục có thực sự tồn tại hay không.
Jrs42

41

Bạn có thể lặp qua tất cả các thư mục bao gồm các hướng dẫn ẩn (bắt đầu bằng dấu chấm) với:

for file in */ .*/ ; do echo "$file is a directory"; done

lưu ý: sử dụng danh sách chỉ */ .*/hoạt động trong zsh nếu có ít nhất một thư mục ẩn trong thư mục. Trong bash nó cũng sẽ hiển thị ...


Một khả năng khác để bash bao gồm các thư mục ẩn sẽ được sử dụng:

shopt -s dotglob;
for file in */ ; do echo "$file is a directory"; done

Nếu bạn muốn loại trừ các liên kết tượng trưng:

for file in */ ; do 
  if [[ -d "$file" && ! -L "$file" ]]; then
    echo "$file is a directory"; 
  fi; 
done

Để chỉ xuất ra tên thư mục theo dõi (A, B, C như đã hỏi) trong mỗi giải pháp, hãy sử dụng tên này trong các vòng lặp:

file="${file%/}"     # strip trailing slash
file="${file##*/}"   # strip path and leading slash
echo "$file is the directoryname without slashes"

Ví dụ (điều này cũng hoạt động với các thư mục chứa khoảng trắng):

mkdir /tmp/A /tmp/B /tmp/C "/tmp/ dir with spaces"
for file in /tmp/*/ ; do file="${file%/}"; echo "${file##*/}"; done

16

Hoạt động với các thư mục chứa khoảng trắng

Lấy cảm hứng từ Sorpigal

while IFS= read -d $'\0' -r file ; do 
    echo $file; ls $file ; 
done < <(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d -print0)

Bài gốc (Không hoạt động với dấu cách)

Lấy cảm hứng từ Boldewyn : Ví dụ về vòng lặp với findlệnh.

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done

9
find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n"

Điều đó cũng tốt và loại bỏ sự cần thiết cho basename. Tôi thích điều này hơn câu trả lời của tôi.
Boldewyn

6

Kỹ thuật tôi sử dụng thường xuyên nhất là find | xargs. Ví dụ: nếu bạn muốn làm cho mọi tệp trong thư mục này và tất cả các thư mục con của nó có thể đọc được trên thế giới, bạn có thể làm:

find . -type f -print0 | xargs -0 chmod go+r
find . -type d -print0 | xargs -0 chmod go+rx

Các -print0tùy chọn kết thúc với một nhân vật NULL thay vì một không gian. Các -0tùy chọn chia đầu vào của nó theo cùng một cách. Vì vậy, đây là sự kết hợp để sử dụng trên các tệp có khoảng trắng.

Bạn có thể hình dung chuỗi lệnh này như nhận mọi đầu ra dòng bằng cách finddán nó vào cuối chmodlệnh.

Nếu lệnh bạn muốn chạy như là đối số của nó ở giữa thay vì ở cuối, bạn phải sáng tạo một chút. Chẳng hạn, tôi cần thay đổi thành mọi thư mục con và chạy lệnh latemk -c. Vì vậy, tôi đã sử dụng (từ Wikipedia ):

find . -type d -depth 1 -print0 | \
    xargs -0 sh -c 'for dir; do pushd "$dir" && latexmk -c && popd; done' fnord

Điều này có tác dụng for dir $(subdirs); do stuff; done, nhưng an toàn cho các thư mục có khoảng trắng trong tên của chúng. Ngoài ra, các lệnh gọi riêng biệt stuffđược thực hiện trong cùng một shell, đó là lý do tại sao trong lệnh của tôi, chúng ta phải quay lại thư mục hiện tại với popd.


3

một vòng lặp bash tối thiểu bạn có thể xây dựng (dựa trên câu trả lời ghostdog74)

for dir in directory/*                                                     
do                                                                                 
  echo ${dir}                                                                  
done

để nén một loạt các tập tin theo thư mục

for dir in directory/*
do
  zip -r ${dir##*/} ${dir}
done                                               

Điều này liệt kê tất cả các tập tin của directory, không chỉ các thư mục con.
dexteritas

2

find . -type d -maxdepth 1


2
Câu trả lời này là tốt, ngoại trừ việc tôi nhận được các đường dẫn đầy đủ, trong khi tôi chỉ muốn tên của các thư mục.
Erik Sapir

2

Nếu bạn muốn thực thi nhiều lệnh trong một vòng lặp for, bạn có thể lưu kết quả của findvới mapfile(bash> = 4) dưới dạng một biến và đi qua mảng với ${dirlist[@]}. Nó cũng hoạt động với các thư mục chứa không gian.

Các findlệnh được dựa trên câu trả lời của Boldewyn. Thông tin thêm về findlệnh có thể được tìm thấy ở đó.

IFS=""
mapfile -t dirlist < <( find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n' )
for dir in ${dirlist[@]}; do
    echo ">${dir}<"
    # more commands can go here ...
done
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.