ls -R
Dizinlerin bir listesini gösterir anlıyorum . Ama neden özyinelemeli? Süreçte özyineleme nasıl kullanılır?
ls
bir dizinle karşılaştığında, bu dizini yinelemeli olarak listelemesidir.
ls -R
Dizinlerin bir listesini gösterir anlıyorum . Ama neden özyinelemeli? Süreçte özyineleme nasıl kullanılır?
ls
bir dizinle karşılaştığında, bu dizini yinelemeli olarak listelemesidir.
Yanıtlar:
Öncelikle, rasgele bir klasör yapısı tanımlayalım:
.
├── a1 [D]
│ ├── b1 [D]
│ │ ├── c1
│ │ ├── c2 [D]
│ │ │ ├── d1
│ │ │ ├── d2
│ │ │ └── d3
│ │ ├── c3
│ │ ├── c4
│ │ └── c5
│ └── b2 [D]
│ ├── c6
│ └── c7
├── a2 [D]
│ ├── b3 [D]
│ │ ├── c8
│ │ └── c9
│ └── b4 [D]
│ ├── c10
│ └── c11
├── a3 [D]
│ ├── b5
│ ├── b6
│ └── b7
└── a4 [D]
Bunu yaptığımızda ls
, sadece temel klasörün çıktısını alırız:
a1 a2 a3 a4
Ancak, aradığımızda ls -R
farklı bir şey buluruz:
.:
a1 a2 a3 a4
./a1:
b1 b2
./a1/b1:
c1 c2 c3 c4 c5
./a1/b1/c2:
d1 d2 d3
./a1/b2:
c6 c7
./a2:
b3 b4
./a2/b3:
c8 c9
./a2/b4:
c10 c11
./a3:
b5 b6 b7
./a4:
Gördüğünüz gibi ls
, temel klasörde ve ardından tüm alt klasörlerde çalışıyor. Ve tüm torun klasörler, reklam sonsuz. Etkili, komut her klasörün geçiyor yinelemeli o dizin ağacının sonuna ulaşıncaya kadar. Bu noktada, ağaçta bir dal oluşturur ve varsa alt klasörler için aynı şeyi yapar.
Veya sözde kodda:
recursivelyList(directory) {
files[] = listDirectory(directory) // Get all files in the directory
print(directory.name + ":\n" + files.join(" ")) // Print the "ls" output
for (file : files) { // Go through all the files in the directory
if (file.isDirectory()) { // Check if the current file being looked at is a directory
recursivelyList(directory) // If so, recursively list that directory
}
}
}
Ve yapabildiğim için, aynı bir Java uygulaması .
Aslında, sormakta olduğunuz birbiriyle yakından ilişkili iki soru var.
ls
? İfadelerinizden ("Süreçte özyineleme nasıl kullanılır?"), Bence bu bilmek istediğiniz şeyin bir parçası. Bu cevap, bu soruyu ele almaktadır.ls
Özyinelemeli bir teknikle uygulanması neden mantıklı :FOLDOC özyinelemeyi şu şekilde tanımlar :
Bir işlev (veya prosedür ) kendisini çağırdığında. Böyle bir fonksiyon "özyinelemeli" olarak adlandırılır. Eğer çağrı bir veya daha fazla başka fonksiyonla yapılırsa, bu fonksiyon grubuna "karşılıklı özyinelemeli" denir.
Uygulamanın doğal yolu, ls
görüntülenecek dosya sistemi girişlerinin bir listesini oluşturan bir fonksiyon yazmak ve yolu ve seçenek argümanlarını işlemek ve girişleri istenen şekilde görüntülemek için diğer kodları yazmaktır. Bu fonksiyonun yinelemeli olarak uygulanması muhtemeldir.
Seçenek işleme sırasında, ls
özyinelemeyle çalışması istenip istenmediğini belirleyecektir ( -R
bayrakla çağrılarak). Eğer öyleyse, girişlerin bir listesini oluşturur fonksiyonu dışında her dizin için bir kez listeleri kendisini arayacak görüntülenecek .
ve ..
. Bu fonksiyonun özyinelemeli ve özyinemeli olmayan versiyonları olabilir veya özyinelemeli çalışması gerekiyorsa, fonksiyon her seferinde kontrol edebilir.
/bin/ls
Çalıştırırken çalışan yürütülebilir dosya olan Ubuntu's , GNU Coreutilsls
tarafından sağlanmaktadır ve birçok özelliğe sahiptir. Sonuç olarak, kodu beklediğinizden biraz daha uzun ve daha karmaşık. Ancak Ubuntu , BusyBox tarafından sağlanan daha basit bir sürümünü de içerir . Bunu yazarak çalıştırabilirsiniz .ls
busybox ls
busybox ls
Özyineleme nasıl kullanılır:ls
BusyBox içinde uygulanır coreutils/ls.c
. scan_and_display_dirs_recur()
Bir dizin ağacını yinelemeli olarak yazdırmak için çağrılan bir işlevi içerir :
static void scan_and_display_dirs_recur(struct dnode **dn, int first)
{
unsigned nfiles;
struct dnode **subdnp;
for (; *dn; dn++) {
if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
if (!first)
bb_putchar('\n');
first = 0;
printf("%s:\n", (*dn)->fullname);
}
subdnp = scan_one_dir((*dn)->fullname, &nfiles);
#if ENABLE_DESKTOP
if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
#endif
if (nfiles > 0) {
/* list all files at this level */
sort_and_display_files(subdnp, nfiles);
if (ENABLE_FEATURE_LS_RECURSIVE
&& (G.all_fmt & DISP_RECURSIVE)
) {
struct dnode **dnd;
unsigned dndirs;
/* recursive - list the sub-dirs */
dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
if (dndirs > 0) {
dnsort(dnd, dndirs);
scan_and_display_dirs_recur(dnd, 0);
/* free the array of dnode pointers to the dirs */
free(dnd);
}
}
/* free the dnodes and the fullname mem */
dfree(subdnp);
}
}
}
Özyinelemeli işlev çağrısının gerçekleştiği çizgi:
scan_and_display_dirs_recur(dnd, 0);
busybox ls
Bir hata ayıklayıcıda çalıştırıyorsanız , bunu işlem sırasında görebilirsiniz . İlk yüklemek ayıklama simgeleri ile -dbgsym.ddeb paketleri sağlayan ve daha sonra yükleme busybox-static-dbgsym
paketi. Ayrıca yükleyin gdb
(bu hata ayıklayıcı).
sudo apt-get update
sudo apt-get install gdb busybox-static-dbgsym
coreutils ls
Basit bir dizin ağacında hata ayıklamayı öneririm .
Eğer bir tane kullanışlı değilseniz, bir tane yapın (bu WinEunuuchs2Unix'in cevabındakimkdir -p
komutla aynı şekilde çalışır ):
mkdir -pv foo/{bar/foobar,baz/quux}
Ve bazı dosyalarla doldurun:
(shopt -s globstar; for d in foo/**; do touch "$d/file$((i++))"; done)
busybox ls -R foo
Çalışmaları beklendiği gibi doğrulayarak bu çıktıyı üretebilirsiniz:
foo:
bar baz file0
foo/bar:
file1 foobar
foo/bar/foobar:
file2
foo/baz:
file3 quux
foo/baz/quux:
file4
busybox
Hata ayıklayıcısında aç :
gdb busybox
GDB kendisi hakkında bazı bilgiler yazdıracak. O zaman şöyle bir şey demeli:
Reading symbols from busybox...Reading symbols from /usr/lib/debug/.build-id/5c/e436575b628a8f54c2a346cc6e758d494c33fe.debug...done.
done.
(gdb)
(gdb)
hata ayıklayıcınızdaki isteminiz nedir. GDB'ye bu komutta yapmasını söyleyeceğiniz ilk şey, scan_and_display_dirs_recur()
fonksiyonun başlangıcında bir kesme noktası belirlemektir :
b scan_and_display_dirs_recur
Bunu çalıştırdığınızda, GDB size şöyle bir şey söylemeli:
Breakpoint 1 at 0x5545b4: file coreutils/ls.c, line 1026.
Şimdi çalıştırmak için GDB anlatmak busybox
ile bağımsız değişkenler olarak (istediğiniz ya da her türlü dizin adı):ls -R foo
run ls -R foo
Bunun gibi bir şey görebilirsiniz:
Starting program: /bin/busybox ls -R foo
Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6c60, first=1) at coreutils/ls.c:1026
1026 coreutils/ls.c: No such file or directory.
Gördüğün No such file or directory
gibi, yukarıdaki gibi, sorun değil. Bu gösterimin amacı sadece scan_and_display_dirs_recur()
fonksiyonun ne zaman çağrıldığını görmek, dolayısıyla GDB'nin gerçek kaynak kodunu incelemesi gerekmiyor.
Hata ayıklayıcının, herhangi bir dizin girişi yazdırılmadan önce bile kesme noktasına ulaştığına dikkat edin. Bu işleve girişte kopar , ancak bu işlevdeki kodun yazdırılması için herhangi bir dizin numaralandırması için çalışması gerekir.
GDB'ye devam etmesini söylemek için, şunları çalıştırın:
c
Her defasında scan_and_display_dirs_recur()
aranır, kesme noktası tekrar vurulur, böylece tekrarı hareket halinde görürsünüz. Buna benziyor ( (gdb)
istemi ve komutları dahil):
(gdb) c
Continuing.
foo:
bar baz file0
Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cb0, first=first@entry=0) at coreutils/ls.c:1026
1026 in coreutils/ls.c
(gdb) c
Continuing.
foo/bar:
file1 foobar
Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cf0, first=first@entry=0) at coreutils/ls.c:1026
1026 in coreutils/ls.c
(gdb) c
Continuing.
foo/bar/foobar:
file2
foo/baz:
file3 quux
Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cf0, first=first@entry=0) at coreutils/ls.c:1026
1026 in coreutils/ls.c
(gdb) c
Continuing.
foo/baz/quux:
file4
[Inferior 1 (process 2321) exited normally]
İşlev recur
adında ... BusyBox yalnızca -R
bayrak verildiğinde kullanıyor mu? Hata ayıklayıcısında bunu bulmak kolaydır:
(gdb) run ls foo
Starting program: /bin/busybox ls foo
Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6c60, first=1) at coreutils/ls.c:1026
1026 in coreutils/ls.c
(gdb) c
Continuing.
bar baz file0
[Inferior 1 (process 2327) exited normally]
Olmasa da -R
, bu özel uygulama uygulaması, ls
hangi dosya sistemi girişlerinin var olduğunu bulmak ve göstermek için aynı işlevi kullanır.
Hata ayıklayıcısını bırakmak istediğinizde, sadece söyle:
q
scan_and_display_dirs_recur()
Kendisini araması gerektiğini nasıl bilebilir:-R
Bayrak gönderildiğinde özellikle nasıl farklı işler ? Kaynak kodu incelemek (Ubuntu sisteminizdeki tam sürüm olmayabilir) G.all_fmt
, hangi seçeneklerin çağrıldığını depoladığı dahili veri yapısını kontrol ettiğini gösterir :
if (ENABLE_FEATURE_LS_RECURSIVE
&& (G.all_fmt & DISP_RECURSIVE)
(BusyBox desteği olmadan derlenmişse -R
, dosya sistemi girişlerini yinelemeli olarak görüntülemeye çalışmaz; ENABLE_FEATURE_LS_RECURSIVE
bölüm budur.)
G.all_fmt & DISP_RECURSIVE
Özyinelemeli işlev çağrısını içeren kod yalnızca doğru olduğunda çalışır.
struct dnode **dnd;
unsigned dndirs;
/* recursive - list the sub-dirs */
dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
if (dndirs > 0) {
dnsort(dnd, dndirs);
scan_and_display_dirs_recur(dnd, 0);
/* free the array of dnode pointers to the dirs */
free(dnd);
}
Aksi takdirde, işlev yalnızca bir kez çalışır (komut satırında belirtilen dizin başına).
Bunu düşündüğünüzde, "özyinelemeli" dizinlerde ve bunların dosyalarında ve dizinlerinde ve dosyalarında ve dizinlerinde ve dosyalarında ve dosyalarında ve dosyalarında hareket eden komutları mantıklı kılar.
.... belirtilen noktadan aşağıya kadar olan tüm ağaç komut tarafından çalıştırılana kadar, bu durumda, altında bulunan tüm alt dizinlerin alt dizinlerinin içeriğini listeleyen ........... komutun argümanları
-R, gevşekçe "tekrar tekrar" olarak adlandırılabilecek özyineleme içindir.
Örneğin bu kodu alın:
───────────────────────────────────────────────────────────────────────────────
$ mkdir -p temp/a
───────────────────────────────────────────────────────────────────────────────
$ mkdir -p temp/b/1
───────────────────────────────────────────────────────────────────────────────
$ mkdir -p temp/c/1/2
───────────────────────────────────────────────────────────────────────────────
$ ls -R temp
temp:
a b c
temp/a:
temp/b:
1
temp/b/1:
temp/c:
1
temp/c/1:
2
temp/c/1/2:
-p
Dizinleri yapımında kitle tek komutla dizinleri oluşturmanıza olanak sağlar. Üst-orta dizinlerden biri veya daha fazlası zaten mevcutsa, bu bir hata değildir ve orta-alt dizinler oluşturulur.
Ardından ls -R
temp ile başlayan ve ağacın altından bütün dallara kadar çalışan her bir dizini özyinelemeli olarak listeler.
Şimdi ls -R
komuta bir tamamlayıcıya bakalım , yani tree
komut:
$ tree temp
temp
├── a
├── b
│ └── 1
└── c
└── 1
└── 2
6 directories, 0 files
Gördüğünüz gibi tree
aynısını ls -R
başarırsak daha özlü ve cüretkar demeye "daha güzel" diyorum.
Şimdi yeni oluşturduğumuz dizinleri tek bir basit komutla tekrar tekrar nasıl kaldıracağınıza bakalım:
$ rm -r temp
Bu özyinelemeli temp
ve altındaki tüm alt dizinleri kaldırır . yani temp/a
, temp/b/1
ve temp/c/1/2
ayrıca aradaki ortadaki dizinleri.
tree
Yine de + 1 . Harika bir araç.
İşte basit bir açıklama, mantıklı çünkü alt dizinlerin içeriğini görüntülemek söz konusu olduğunda, aynı fonksiyon zaten bir dizin ile ne yapılacağını biliyor. Bu nedenle, bu sonucu almak için her bir alt dizinde kendisini çağırması yeterlidir!
Sahte kodda şöyle görünür:
recursive_ls(dir)
print(files and directories)
foreach (directoriy in dir)
recursive_ls(directory)
end foreach
end recursive_ls