Bagaimana cara saya menelusuri hanya direktori di bash?

251

Saya memiliki folder dengan beberapa direktori dan beberapa file (ada yang disembunyikan, dimulai dengan titik).

for d in *; do
 echo $d
done

akan mengulang semua file, tetapi saya hanya ingin mengulang melalui direktori. Bagaimana aku melakukan itu?

rubo77
sumber

Jawaban:

334

Anda dapat menentukan garis miring di akhir agar hanya cocok dengan direktori:

for d in */ ; do
    echo "$d"
done
choroba
sumber
7
Perhatikan bahwa itu juga termasuk symlink ke direktori.
Stéphane Chazelas
1
jadi bagaimana Anda mengecualikan symlink?
rubo77
3
@ rubo77: Anda dapat menguji [[ -L $d ]]apakah $ d adalah tautan simbolik.
choroba
1
@ AsymLabs Itu salah, set -Phanya memengaruhi perintah yang mengubah direktori. sprunge.us/TNac
Chris Down
4
@choroba: [[ -L "$f" ]]tidak akan mengecualikan symlink dalam hal ini dengan */, Anda harus menghapus garis miring dengan [[ -L "${f%/}" ]](lihat Tes untuk tautan dengan garis miring )
rubo77
78

Anda dapat menguji dengan -d:

for f in *; do
    if [ -d "$f" ]; then
        # $f is a directory
    fi
done

Ini adalah salah satu operator pengujian file .

goldilocks
sumber
8
Perhatikan bahwa ini akan menyertakan symlink ke direktori.
Stéphane Chazelas
21
if [[ -d "$f" && ! -L "$f" ]]akan mengecualikan symlink
rubo77
Akan rusak pada direktori kosong. if [[ "$f" = "*" ]]; then continue; fi
Piskvor
30

Waspadalah bahwa solusi choroba, meskipun elegan, dapat menimbulkan perilaku yang tidak terduga jika tidak ada direktori yang tersedia dalam direktori saat ini. Dalam keadaan ini, alih-alih melewatkan forloop, bash akan menjalankan loop tepat sekali di mana dsama dengan */:

#!/usr/bin/env bash

for d in */; do
    # Will print */ if no directories are available
    echo $d
done

Saya sarankan menggunakan yang berikut ini untuk melindungi dari kasus ini:

#!/usr/bin/env bash

for f in *; do
    if [ -d ${f} ]; then
        # Will not run if no directories are available
        echo $f
    fi
done

Kode ini akan mengulang semua file dalam direktori saat ini, memeriksa apakah fdirektori, lalu menggema fjika kondisinya kembali benar. Jika fsama dengan */, echo $ftidak akan dieksekusi.

emagdne
sumber
3
Jauh lebih mudah shopt -s nullglob.
choroba
21

Jika Anda perlu memilih file yang lebih spesifik daripada hanya menggunakan direktori finddan meneruskannya ke while read:

shopt -s dotglob
find * -prune -type d | while IFS= read -r d; do 
    echo "$d"
done

Gunakan shopt -u dotglobuntuk mengecualikan direktori tersembunyi (atau setopt dotglob/ unsetopt dotglobdi zsh).

IFS=untuk menghindari pemisahan nama file yang mengandung salah satu $IFS, misalnya:'a b'

lihat jawaban AsymLabs di bawah ini untuk findopsi lebih lanjut


sunting:
Jika Anda perlu membuat nilai keluar dari dalam loop sementara, Anda dapat menghindari subkulit tambahan dengan trik ini:

while IFS= read -r d; do 
    if [ "$d" == "something" ]; then exit 1; fi
done < <(find * -prune -type d)
rubo77
sumber
2
Saya menemukan solusi ini di: stackoverflow.com/a/8489394/1069083
rubo77
11

Anda dapat menggunakan bash murni untuk itu, tetapi lebih baik menggunakan find:

find . -maxdepth 1 -type d -exec echo {} \;

(temukan tambahan akan termasuk direktori tersembunyi)

buru-buru
sumber
8
Perhatikan bahwa itu tidak termasuk symlink ke direktori. Anda dapat menggunakan shopt -s dotglobuntuk bashmemasukkan direktori tersembunyi. Anda juga akan termasuk .. Perhatikan juga bahwa -maxdepthini bukan opsi standar -prune.
Stéphane Chazelas
2
The dotglobpilihan adalah menarik tapi dotglobhanya berlaku untuk penggunaan *. find .akan selalu menyertakan direktori tersembunyi (dan direktori saat ini juga)
rubo77
6

Ini dilakukan untuk menemukan direktori yang terlihat dan tersembunyi di dalam direktori kerja saat ini, tidak termasuk direktori root:

untuk hanya mengulang direktori:

 find -path './*' -prune -type d

untuk memasukkan symlink dalam hasil:

find -L -path './*' -prune -type d

untuk melakukan sesuatu pada setiap direktori (tidak termasuk symlinks):

find -path './*' -prune -type d -print0 | xargs -0 <cmds>

untuk mengecualikan direktori tersembunyi:

find -path './[^.]*' -prune -type d

untuk menjalankan beberapa perintah pada nilai yang dikembalikan (contoh yang sangat dibuat-buat):

find -path './[^.]*' -prune -type d -print0 | xargs -0 -I '{}' sh -c \
"printf 'first: %-40s' '{}'; printf 'second: %s\n' '{}'"

alih-alih 'sh -c' juga dapat menggunakan 'bash -c', dll.

AsymLabs
sumber
Apa yang terjadi jika gema '* /' in 'untuk d in gema * /' berisi, katakanlah, 60.000 direktori?
AsymLabs
Anda akan mendapatkan kesalahan "Terlalu banyak file" tetapi itu bisa diatasi: Cara menghindari "Terlalu banyak file yang terbuka" di debian
rubo77
1
Contoh xargs hanya akan berfungsi untuk subset nama direktori 9e.g. yang memiliki spasi atau baris baru tidak akan berfungsi). Lebih baik digunakan ... -print0 | xargs -0 ...jika Anda tidak tahu nama sebenarnya.
Anthon
1
@ rubo77 menambahkan cara untuk mengecualikan file / direktori tersembunyi dan eksekusi beberapa perintah - tentu saja ini dapat dilakukan dengan skrip juga.
AsymLabs
1
Saya menambahkan contoh dalam jawaban saya di bagian bawah, bagaimana melakukan sesuatu untuk setiap direktori menggunakan fungsi denganfind * | while read file; do ...
rubo77
3

Anda dapat mengulang semua direktori termasuk direktori tersembunyi (dimulai dengan titik) dalam satu baris dan beberapa perintah dengan:

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

catatan: menggunakan daftar */ .*/berfungsi di bash, tetapi juga menampilkan folder .dan ..sementara di zsh tidak akan menampilkan ini tetapi membuang kesalahan jika tidak ada file tersembunyi di folder

Versi yang lebih bersih yang akan menyertakan direktori tersembunyi dan mengecualikan ../ akan dengan opsi dotglob:

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

(atau setopt dotglobdalam zsh)

Anda dapat menghapus dotglob dengan

shopt -u dotglob
rubo77
sumber
2

Ini akan mencakup jalur lengkap di setiap direktori dalam daftar:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done
rubo77
sumber
1
istirahat saat menggunakan folder dengan spasi
Alejandro Sazo
0

Gunakan finddengan -execuntuk mengulang direktori dan memanggil fungsi di opsi exec:

dosomething () {
  echo "doing something with $1"
}
export -f dosomething
find -path './*' -prune -type d -exec bash -c 'dosomething "$0"' {} \;

Gunakan shopt -s dotglobatau shopt -u dotglobuntuk memasukkan / mengecualikan direktori tersembunyi

rubo77
sumber
0

Ini daftar semua direktori bersama dengan jumlah sub-direktori di jalur yang diberikan:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done
pabloa98
sumber
-1
ls -d */ | while read d
do
        echo $d
done
dcat
sumber
This is one directory with spaces in the name- tetapi diuraikan sebagai beberapa.
Piskvor
-5
ls -l | grep ^d

atau:

ll | grep ^d

Anda dapat mengaturnya sebagai alias

pengguna250676
sumber
1
Sayangnya, saya tidak berpikir ini menjawab pertanyaan, yang adalah "Saya ingin mengulang hanya melalui direktori" - yang sedikit berbeda dari lsjawaban berbasis ini , yang berisi daftar direktori.
Jeff Schaller
1
Tidak pernah merupakan ide yang baik untuk mengurai output dari ls: mywiki.wooledge.org/ParsingLs
codeforester