Mam serwer, który każdego dnia otrzymuje plik do klienta do katalogu. Nazwy plików są zbudowane w następujący sposób:
uuid_datestring_other-data
Na przykład:
d6f60016-0011-49c4-8fca-e2b3496ad5a7_20160204_023-ERROR
uuid
jest uuid w standardowym formacie.datestring
jest wyjściem zdate +%Y%m%d
.other-data
ma zmienną długość, ale nigdy nie będzie zawierać podkreślenia.
Mam plik w formacie:
#
d6f60016-0011-49c4-8fca-e2b3496ad5a7 client1
d5873483-5b98-4895-ab09-9891d80a13da client2
be0ed6a6-e73a-4f33-b755-47226ff22401 another_client
...
Muszę sprawdzić, czy każdy identyfikator użytkownika wymieniony w pliku ma odpowiedni plik w katalogu, używając bash.
Zaszedłem tak daleko, ale czuję, że idę z niewłaściwego kierunku, używając instrukcji if, i że muszę przeglądać pliki w katalogu źródłowym.
Zmienne source_directory i uuid_list zostały wcześniej przypisane w skrypcie:
# Check the entries in the file list
while read -r uuid name; do
# Ignore comment lines
[[ $uuid = \#* ]] && continue
if [[ -f "${source_directory}/${uuid}*" ]]
then
echo "File for ${name} has arrived"
else
echo "PANIC! - No File for ${name}"
fi
done < "${uuid_list}"
Jak powinienem sprawdzić, czy pliki na mojej liście istnieją w katalogu? Chciałbym w miarę możliwości korzystać z funkcji bash, ale nie jestem przeciwny używaniu poleceń, jeśli to konieczne.
command-line
bash
scripts
Arroniczny
źródło
źródło
Odpowiedzi:
Przejdź po plikach, utwórz tablicę asocjacyjną nad uuidami zawartymi w ich nazwach (użyłem rozszerzenia parametrów, aby wyodrębnić uuid). Przeczytaj listę, sprawdź tablicę asocjacyjną dla każdego identyfikatora użytkownika i zgłoś, czy plik został nagrany, czy nie.
źródło
cd
wejść do katalogu w skrypcie, ale po prostu zastanawiałem się nad tym, aby zdobyć wiedzę.file=${file##*/}
.Oto bardziej „nieśmiałe” i zwięzłe podejście:
Zauważ, że chociaż powyższe jest ładne i będzie działać dobrze dla kilku plików, jego szybkość zależy od liczby UUID i będzie bardzo wolna, jeśli będziesz musiał przetworzyć wiele. W takim przypadku skorzystaj z rozwiązania @ choroba lub, dla czegoś naprawdę szybkiego, uniknij powłoki i wywołaj
perl
:Aby zilustrować różnice czasowe, przetestowałem moje podejście do bash, chorobę i mojego perla na pliku z 20000 UUID, z których 18001 miało odpowiednią nazwę pliku. Zauważ, że każdy test był uruchamiany przez przekierowanie wyjścia skryptu do
/dev/null
.Moje uderzenie (~ 3,5 min)
Choroba (uderzenie, ~ 0,7 s)
Mój perl (~ 0,1 s):
źródło
cd
przejść do katalogu w skrypcie, ale czy istnieje metoda, dzięki której ścieżka wyszukiwania może zostać uwzględniona podczas wyszukiwania?${source_directory}
co robiłeś w skrypcie."$2"
i przekaż go do skryptu jako drugi argument.To jest czysta Bash (tj. Bez zewnętrznych poleceń) i jest to najbardziej spójne podejście, jakie mogę wymyślić.
Ale pod względem wydajności nie jest tak naprawdę dużo lepszy niż obecnie.
Odczyta każdą linię z
path/to/file
; dla każdej linii, będzie przechowywać w pierwsze pole$uuid
i wypisuje komunikat, jeśli plik pasujący do wzorcapath/to/directory/$uuid*
jest nie znaleziono:Zadzwoń za pomocą
path/to/script path/to/file path/to/directory
.Przykładowe dane wyjściowe przy użyciu przykładowego pliku wejściowego w pytaniu w hierarchii katalogu testowego zawierającego przykładowy plik w pytaniu:
źródło
Chodzi o to, aby nie martwić się o zgłaszanie błędów, które powłoka zgłosi za Ciebie. Jeśli spróbujesz
<
otworzyć plik, który nie istnieje, twoja powłoka narzeka. W rzeczywistości wstawi on skrypt$0
i numer wiersza, w którym wystąpił błąd, do wyjścia błędu, kiedy to robi ... To dobra informacja, która jest już domyślnie podawana - więc nie przejmuj się.Nie musisz także umieszczać pliku tak po linii - może być strasznie powolny. To rozszerza całość w jednym ujęciu do tablicy argumentów rozdzielonej spacjami i obsługuje dwa jednocześnie. Jeśli twoje dane są zgodne z twoim przykładem, to
$1
zawsze będzie twój identyfikator użytkownika i$2
będzie twój$name
. Jeślibash
można otworzyć dopasowanie do Twojego UUID - i istnieje tylko jedno takie dopasowanie - to sięprintf
dzieje. W przeciwnym razie tak się nie stanie, a powłoka wypisze diagnostykę do stderr o tym, dlaczego.źródło
unset IFS
zapewnia$(cat <uuid_file)
podział na białe znaki. Pociski dzielą się$IFS
inaczej, gdy składają się tylko z białej spacji lub są rozbrojone. Takie dzielone rozwinięcia nigdy nie mają żadnych pól zerowych, ponieważ wszystkie sekwencje białych znaków są tylko pojedynczymi ogranicznikami pól. Tak długo, jak w każdym wierszu znajdują się tylko dwa pola oddzielone spacjami, to powinno działać. wbash
każdym razie.set -f
zapewnia, że niecytowane rozwinięcie nie jest interpretowane dla globów, a set + f gwarantuje, że późniejsze globusy są.<>
ponieważ tworzy to nieistniejący plik.<
zgłoś się tak, jak chciałem. możliwym problemem z tym - i powodem, dla którego niewłaściwie użyłem go<>
w pierwszej kolejności - jest to, że jeśli jest to plik potokowy bez czytnika lub podobny do char dev-line buforowanego, zawiesi się. można tego uniknąć, posługując się wyjściem błędu w sposób bardziej wyraźny i działając[ -f "$dir/$1"* ]
. mówimy tutaj o UUID-ach, więc nigdy nie powinno się rozszerzać do więcej niż jednego pliku. jest to całkiem miłe, ale to, jak zgłasza stderr nazwy nieudanych plików.<>
nadal byłby użyteczny w ten sposób ...<>
lepiej, jeśli glob może rozwinąć się do katalogu, ponieważ w systemie Linux odczyt / zapis będzie nie powiedz i powiedz - to katalog.bash
akceptuje glob przekierowania tylko, jeśli pasuje tylko do jednego pliku. patrzman bash
REDIRECTION.Podejdę do tego, aby najpierw pobrać UUID z pliku, a następnie użyć
find
Dla gotowości
Przykład z listą plików w
/etc/
poszukiwaniu nazw plików passwd, group, fstab i THISDOESNTEXIST.Ponieważ wspomniałeś, że katalog jest płaski, możesz użyć
-printf "%f\n"
opcji, aby po prostu wydrukować samą nazwę plikuNie robi to, aby wyświetlić listę brakujących plików.
find
niewielką wadą jest to, że nie mówi ci, jeśli nie znajdzie pliku, tylko gdy coś pasuje. Można jednak sprawdzić dane wyjściowe - jeśli dane wyjściowe są puste, to brakuje nam plikuBardziej czytelny:
A oto jak działa jako mały skrypt:
Można użyć
stat
jako alternatywy, ponieważ jest to płaski katalog, ale poniższy kod nie będzie działał rekurencyjnie dla podkatalogów, jeśli kiedykolwiek zdecydujesz się je dodać:Jeśli weźmiemy ten
stat
pomysł i uruchomimy go, możemy użyć kodu wyjścia stat jako wskazania, czy plik istnieje, czy nie. Skutecznie chcemy to zrobić:Przykładowy przebieg:
źródło