Bagaimana cara mengulang direktori di Linux?

168

Saya sedang menulis skrip dalam bash di Linux dan harus melalui semua nama subdirektori dalam direktori yang diberikan. Bagaimana saya bisa mengulang direktori ini (dan melewati file biasa)?

Sebagai contoh:
direktori yang diberikan adalah /tmp/
ia memiliki subdirektori berikut:/tmp/A, /tmp/B, /tmp/C

Saya ingin mengambil A, B, C.

Erik Sapir
sumber

Jawaban:

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

Penjelasan singkat:

  • find menemukan file (cukup jelas)

  • .adalah direktori saat ini, yang setelah cditu /tmp(IMHO ini lebih fleksibel daripada memiliki /tmplangsung dalam findperintah. Anda hanya memiliki satu tempat, yaitu cd, untuk berubah, jika Anda ingin lebih banyak tindakan terjadi di folder ini)

  • -maxdepth 1dan -mindepth 1pastikan itu findhanya terlihat di direktori saat ini dan tidak termasuk .dalam hasilnya

  • -type d hanya mencari direktori

  • -printf '%f\n hanya mencetak nama folder yang ditemukan (plus baris baru) untuk setiap klik.

Dan lagi!

Boldewyn
sumber
Omong-omong: Perhatikan, bahwa semua jawaban juga akan menemukan folder tersembunyi (yaitu, folder yang dimulai dengan titik)! Jika Anda tidak menginginkan ini, tambahkan `-regex '\ ./ [^ \.]. *'` Sebelum opsi printf atau exec.
Boldewyn
1
Bermanfaat - jika Anda hanya perlu menjalankan satu perintah untuk setiap hit ;-) - Hanya saya yang memiliki beberapa perintah untuk menjalankan :-(.
Martin
Tidak masalah: Adaptasikan solusi ini: transnum.blogspot.de/2008/11/... Di dalam while..doneloop Anda bisa menjadi gila.
Boldewyn
1
Ini adalah metode yang sangat bagus untuk beberapa kasus penggunaan; find's -execpilihan memungkinkan Anda menjalankan perintah apapun untuk setiap file / direktori.
jtpereyda
451

Semua jawaban sejauh ini digunakan find, jadi ini satu-satunya jawaban. Tidak perlu alat eksternal dalam kasus Anda:

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
ghostdog74
sumber
18
+1 - Mengapa repot dengan findketika Anda dapat menambahkan tebasan ke wildcard
Tobias Kienzler
19
begitu for dir in */; do echo $dir; donejuga untuk direktori di direktori saat ini.
Ehtesh Choudhury
41
Alangkah baiknya jika bisa memuat penjelasan apa dir=${dir%*/}dan apa yang echo ${dir##*/}sedang dilakukan.
Jeremy S.
11
Itu karena dir variabel akan menyertakan backslash di akhir. Dia hanya menghapusnya untuk memiliki nama yang bersih. % akan menghapus / di akhir. ## akan menghapus / di awal. Ini adalah operator untuk menangani substring.
sfratini
8
Jika tidak ada kecocokan, loop akan dipicu /tmp/*/, akan lebih bijaksana untuk menyertakan cek untuk melihat apakah direktori tersebut benar-benar ada.
Jrs42
41

Anda dapat mengulangi semua direktori termasuk direktori tersembunyi (dimulai dengan titik) dengan:

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

Catatan: menggunakan daftar hanya */ .*/berfungsi di zsh jika ada setidaknya satu direktori tersembunyi di folder. Dalam bash itu akan ditampilkan juga .dan..


Kemungkinan lain untuk bash untuk memasukkan direktori tersembunyi adalah menggunakan:

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

Jika Anda ingin mengecualikan symlink:

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

Untuk menampilkan hanya nama direktori tambahan (A, B, C seperti yang dipertanyakan) di setiap solusi gunakan ini di dalam loop:

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

Contoh (ini juga berfungsi dengan direktori yang berisi spasi):

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

Bekerja dengan direktori yang berisi spasi

Terinspirasi oleh 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)

Pos asli (Tidak berfungsi dengan spasi)

Terinspirasi oleh Boldewyn : Contoh loop dengan findperintah.

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done
zpon
sumber
9
find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n"
Ignacio Vazquez-Abrams
sumber
Itu bagus juga dan menghilangkan kebutuhan basename. Saya lebih suka ini daripada jawaban saya.
Boldewyn
6

Teknik yang paling sering saya gunakan adalah find | xargs. Misalnya, jika Anda ingin membuat setiap file dalam direktori ini dan semua subdirektori yang dapat dibaca dunia, Anda dapat melakukannya:

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

The -print0pilihan berakhir dengan karakter NULL bukan spasi. The -0pilihan membagi input dengan cara yang sama. Jadi ini adalah kombinasi yang digunakan pada file dengan spasi.

Anda dapat menggambarkan rantai perintah ini dengan mengambil setiap output baris dengan finddan menempelkannya di akhir chmodperintah.

Jika perintah yang ingin Anda jalankan sebagai argumennya di tengah alih-alih di akhir, Anda harus sedikit kreatif. Sebagai contoh, saya perlu mengubah ke setiap subdirektori dan menjalankan perintah latemk -c. Jadi saya menggunakan (dari Wikipedia ):

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

Ini memiliki efek for dir $(subdirs); do stuff; done, tetapi aman untuk direktori dengan spasi di namanya. Selain itu, panggilan terpisah untuk stuffdibuat dalam shell yang sama, itulah sebabnya dalam perintah saya kita harus kembali ke direktori saat ini dengan popd.

Matthew Leingang
sumber
3

loop bash minimal yang bisa Anda buat (berdasarkan jawaban ghostdog74)

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

untuk zip sejumlah file dengan direktori

for dir in directory/*
do
  zip -r ${dir##*/} ${dir}
done                                               
Harry Moreno
sumber
Ini mencantumkan semua file directory, bukan hanya subdirektori.
dexteritas
2

find . -type d -maxdepth 1

Anonim
sumber
2
jawaban ini bagus, kecuali bahwa saya menerima path lengkap, sementara saya hanya menginginkan nama direktori.
Erik Sapir
2

Jika Anda ingin menjalankan beberapa perintah dalam for for, Anda dapat menyimpan hasil finddengan mapfile(bash> = 4) sebagai variabel dan melalui array dengan${dirlist[@]} . Ini juga berfungsi dengan direktori yang berisi spasi.

The findperintah didasarkan pada jawaban oleh Boldewyn. Informasi lebih lanjut tentang findperintah dapat ditemukan di sana.

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
dexteritas
sumber