W przypadku implementacji programu findr opierającego się na funkcjonalności klasycznego narzędzia find w systemie Unix, podstawową rzeczą jest zapewnienie odpowiednich argumentów pozycyjnych, które wskazują ścieżki do przeszukania. Każda ze wskazanych ścieżek będzie przeszukiwana rekurencyjnie, a program będzie zwracał listę znalezionych plików i katalogów. W przypadku, gdy znajdujemy się w katalogu tests/inputs i wskażemy . (kropkę) jako aktualny katalog roboczy, findr zwróci wszystkie zawartości tego katalogu. Warto zauważyć, że sposób sortowania wyników w systemie macOS różni się od tego, co oferuje wersja GNU w systemie Linux, co może prowadzić do różnic w wynikach, choć logika działania programu pozostaje niezmienna.

Przykładem podstawowego działania może być polecenie:

bash
$ find .
./g.csv ./a/a.txt ./a/b/b.csv ./a/b/c/c.mp3 ./f/f.txt ./d/d.txt ./d/d.tsv ./d/e/e.mp3

Podstawowe opcje, które można użyć w programie to m.in. -type, które pozwala na wskazanie typu poszukiwanego pliku. Na przykład, aby znaleźć tylko pliki regularne, należy użyć opcji -type f, co zwróci jedynie pliki, a nie katalogi ani linki symboliczne:

bash
$ find . -type f
./g.csv ./a/a.txt ./a/b/b.csv ./a/b/c/c.mp3 ./f/f.txt ./d/d.txt ./d/d.tsv ./d/e/e.mp3

Możemy również ograniczyć wyszukiwanie do linków symbolicznych za pomocą opcji -type l:

bash
$ find . -type l ./d/b.csv

Inną przydatną opcją jest -name, która pozwala na dopasowanie do wzorca, na przykład:

bash
$ find . -name \*.csv
./g.csv ./a/b/b.csv ./d/b.csv

Dodatkowo, za pomocą operatora -o możemy połączyć kilka wzorców w jedno zapytanie. Przykładowo, aby znaleźć wszystkie pliki .txt lub .csv, możemy użyć:

bash
$ find . -name "*.txt" -o -name "*.csv" ./g.csv ./a/a.txt ./a/b/b.csv ./f/f.txt ./d/b.csv ./d/d.txt

Jeżeli chcemy ograniczyć wyniki do plików lub linków symbolicznych, stosujemy kombinację opcji -type i -name:

bash
$ find . -name "*.csv" -type f -o -type l
./g.csv ./a/b/b.csv ./d/b.csv

Warto pamiętać, że w przypadku używania wyrażeń z wieloma operatorami logicznymi, takich jak -o, należy odpowiednio grupować argumenty w nawiasach, by uniknąć nieporozumień w składni:

bash
$ find . \( -type f -o -type l \) -name "*.csv" ./g.csv ./a/b/b.csv ./d/b.csv

Kolejną ważną funkcjonalnością jest możliwość określenia wielu ścieżek do przeszukania. Można to zrobić, podając je jako argumenty pozycyjne:

bash
$ find a/b d -name "*.mp3"
a/b/c/c.mp3 d/e/e.mp3

Jeśli podana ścieżka nie istnieje, findr zgłosi błąd:

arduino
$ find blargh find: blargh: No such file or directory

W przypadku, gdy napotka katalog, do którego nie ma dostępu (np. przez brak uprawnień), findr wypisuje odpowiedni komunikat:

bash
$ mkdir cant-touch-this && chmod 000 cant-touch-this
$ find . -type d ./a ./a/b ./a/b/c ./f find: ./cant-touch-this: Permission denied

Warto jednak pamiętać, że taka sytuacja występuje jedynie w systemach Unix. Windows nie posiada tego typu systemu uprawnień, więc problem nie występuje.

W dalszej części implementacji warto posługiwać się bibliotekami do parsowania argumentów wiersza poleceń oraz zarządzania zależnościami. Do tego celu przydatne będą pakiety takie jak clap, który umożliwia definiowanie argumentów w sposób zorganizowany i przejrzysty, oraz regex, pozwalający na korzystanie z wyrażeń regularnych w celu dopasowania wzorców.

Przykład definicji argumentów w pliku Cargo.toml:

ini
[dependencies]
anyhow = "1.0.79" clap = { version = "4.5.0", features = ["derive"] } regex = "1.10.3" walkdir = "2.4.0"

Biblioteka walkdir pozwala na rekurencyjne przeszukiwanie katalogów. Ważne jest również, aby odpowiednio obsługiwać przypadki testowe, uwzględniając linki symboliczne w odpowiednich katalogach, które mogą stanowić wyzwanie w niektórych systemach operacyjnych. Dobrą praktyką jest stworzenie odpowiedniego skryptu bashowego, który zadba o odpowiednie kopiowanie katalogów z testami oraz o poprawne zarządzanie linkami symbolicznymi.

Kiedy program jest gotowy do działania, możliwe jest uruchomienie go z poziomu wiersza poleceń, korzystając z predefiniowanej składni:

sql
$ cargo run -- --help Rust version of `find` Usage: findr [OPTIONS] [PATH]... Arguments: [PATH]... Search paths [default: .] Options: -n, --name [...] Name -t, --type [...] Entry type [possible values: d, f, l] -h, --help Print help -V, --version Print version

Program powinien być w stanie obsługiwać różne kombinacje argumentów, umożliwiając użytkownikowi dokładne określenie typu pliku, katalogu czy linku, a także wzorców nazw, które będą przeszukiwane.

Należy także zwrócić uwagę na odpowiednie zarządzanie błędami i komunikatami, szczególnie w przypadku napotkania na nieistniejące katalogi lub brak dostępu do niektórych plików. Program powinien w takich przypadkach informować użytkownika o problemie, ale kontynuować przeszukiwanie innych ścieżek.

Jak działają opcje ls w systemie Linux?

Program ls w systemach Unix/Linux jest jednym z najczęściej używanych narzędzi do wyświetlania zawartości katalogów. Jego podstawowe funkcjonalności obejmują pokazanie nazw plików, zarówno plików, jak i katalogów, a także szczegółowych informacji o tych plikach, takich jak uprawnienia, właściciel, rozmiar, data modyfikacji, i inne. Komenda ls posiada wiele opcji, które umożliwiają dostosowanie wyjścia do potrzeb użytkownika, ale w tej części skupimy się na najistotniejszych funkcjach, które najczęściej są wykorzystywane.

Jeśli komenda ls zostanie wywołana bez żadnych argumentów, domyślnie wyświetli zawartość bieżącego katalogu. Natomiast, gdy użytkownik poda nazwy konkretnych katalogów lub plików, ls wyświetli tylko ich zawartość. Pliki, które są katalogami, zostaną wyświetlone z informacjami o zawartości, natomiast pliki innych typów – z ich nazwą i dodatkowymi szczegółami.

Na przykład, aby wyświetlić zawartość katalogu w systemie, wystarczy wpisać:

shell
$ ls
Cargo.toml set-test-perms.sh* src/ tests/

Jeśli użytkownik chce uzyskać bardziej szczegółowe informacje na temat plików, powinien skorzystać z opcji -l (long listing). Ta opcja pokazuje szczegółowe dane o każdym pliku, takie jak:

  • tryb dostępu do pliku,

  • liczba dowiązań (linków),

  • właściciel pliku,

  • grupa właściciela,

  • rozmiar pliku,

  • data i godzina ostatniej modyfikacji,

  • ścieżka do pliku.

Przykład użycia opcji -l:

diff
$ ls -l total 16 -rw-r--r-- 1 kyclark staff 217 Aug 11 08:26 Cargo.toml -rwxr-xr-x 1 kyclark staff 447 Aug 12 17:56 set-test-perms.sh* drwxr-xr-x 5 kyclark staff 160 Aug 26 09:44 src/ drwxr-xr-x 4 kyclark staff 128 Aug 17 08:42 tests/

Dodatkowo, często używaną opcją jest -a (all), która pokazuje także pliki ukryte – te, których nazwy zaczynają się od kropki (np. .git). Pliki te zazwyczaj zawierają dane konfiguracyjne lub stan programu.

Przykład użycia -a:

shell
$ ls -a
. .. Cargo.toml src/ set-test-perms.sh* tests/

Warto zauważyć, że pliki ukryte, takie jak .git czy .gitignore, są często używane przez systemy kontroli wersji do przechowywania metadanych dotyczących projektu. Również konfiguracja użytkownika może być przechowywana w ukrytych plikach (np. .bashrc, .zshrc).

Komenda ls obsługuje także argumenty wskazujące konkretne pliki lub katalogi. Można określić je bezpośrednio w poleceniu, aby wyświetlić tylko ich zawartość:

makefile
$ ls src/ tests/
src/: main.rs owner.rs tests/: cli.rs inputs

Jeśli użytkownik chce zobaczyć zawartość katalogów z filtrami, można użyć opcji -l w połączeniu z rozszerzeniem (np. wyszukiwanie plików o rozszerzeniu .rs):

bash
$ ls -l src/*.rs -rw-r--r-- 1 kyclark staff 8959 Feb 25 12:09 src/main.rs -rw-r--r-- 1 kyclark staff 313 Feb 25 12:03 src/owner.rs

Warto zwrócić uwagę na różnice w porządku wyświetlania plików pomiędzy różnymi systemami operacyjnymi. Na przykład, na macOS plik .hidden zostanie wyświetlony na początku listy, podczas gdy w systemie Linux będzie znajdować się na końcu.

Przykład z systemu macOS:

shell
$ ls -la tests/inputs/
total 16 drwxr-xr-x 7 kyclark staff 224 Aug 12 10:29 ./ drwxr-xr-x 4 kyclark staff 128 Aug 17 08:42 ../ -rw-r--r-- 1 kyclark staff 0 Mar 19 2021 .hidden -rw-r--r-- 1 kyclark staff 193 May 31 16:43 bustle.txt

A oto przykład z systemu Linux:

diff
$ ls -la tests/inputs/ total 20 drwxr-xr-x. 3 kyclark staff 4096 Aug 21 12:13 ./ drwxr-xr-x. 3 kyclark staff 4096 Aug 21 12:13 ../ -rw-r--r--. 1 kyclark staff 193 Aug 21 12:13 bustle.txt -rw-r--r--. 1 kyclark staff 0 Aug 21 12:13 empty.txt -rw-r--r--. 1 kyclark staff 0 Aug 21 12:13 .hidden

Te różnice w porządku wyświetlania wyników mogą mieć znaczenie w zależności od tego, jak użytkownik chce analizować dane.

Również warto pamiętać, że błędy związane z nieistniejącymi plikami lub katalogami są zazwyczaj wyświetlane na początku. Na przykład, jeżeli podamy plik, który nie istnieje, komenda ls zwróci błąd:

bash
$ ls Cargo.toml blargh src/main.rs
ls: blargh: No such file or directory Cargo.toml src/main.rs

Warto przy tym zauważyć, że chociaż ls jest jednym z najstarszych narzędzi systemowych, jego funkcje nie zmieniały się drastycznie od czasów pierwszego Unix-a. Mimo że narzędzia BSD i GNU rozwinęły funkcje ls w różnorodny sposób, narzędzie to wciąż pełni swoje podstawowe zadanie w systemach operacyjnych.

Dzięki temu, że ls oferuje różnorodne opcje wyświetlania zawartości katalogów i plików, jest to bardzo potężne narzędzie, które w połączeniu z odpowiednimi parametrami, może pomóc w dokładnym analizowaniu systemu plików. Jednakże pełne zrozumienie działania tego narzędzia jest istotne nie tylko dla codziennego użytkownika, ale i dla programistów, którzy mogą chcieć stworzyć narzędzie podobne do ls w językach takich jak Rust, aby lepiej zrozumieć działanie systemów operacyjnych i przechowywania danych.