Próbuję znaleźć najbardziej skuteczny sposób na iterację pewnych wartości, które są stałą liczbą wartości od siebie na liście słów oddzielonych spacjami (nie chcę używać tablicy). Na przykład,
list="1 ant bat 5 cat dingo 6 emu fish 9 gecko hare 15 i j"
Chcę więc móc iterować po liście i uzyskiwać dostęp tylko do 1,5,6,9 i 15.
EDYCJA: Powinienem był jasno powiedzieć, że wartości, które próbuję uzyskać z listy, nie muszą różnić się formatem od reszty listy. Tym, co je wyróżnia, jest wyłącznie ich pozycja na liście (w tym przypadku pozycja 1,4,7 ...). Tak więc lista może być,1 2 3 5 9 8 6 90 84 9 3 2 15 75 55
ale nadal chcę te same liczby. Chcę też móc to zrobić, zakładając, że nie znam długości listy.
Metody, o których do tej pory myślałem, to:
Metoda 1
set $list
found=false
find=9
count=1
while [ $count -lt $# ]; do
if [ "${@:count:1}" -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
done
Metoda 2
set list
found=false
find=9
while [ $# ne 0 ]; do
if [ $1 -eq $find ]; then
found=true
break
fi
shift 3
done
Metoda 3 Jestem pewien, że pipowanie czyni tę opcję najgorszą, ale próbowałem znaleźć metodę, która nie używa zestawu, z ciekawości.
found=false
find=9
count=1
num=`echo $list | cut -d ' ' -f$count`
while [ -n "$num" ]; do
if [ $num -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
num=`echo $list | cut -d ' ' -f$count`
done
Co byłoby najbardziej wydajne, czy brakuje mi prostszej metody?
źródło
Odpowiedzi:
Całkiem proste z
awk
. Otrzymasz wartość co czwarte pole do wprowadzania dowolnej długości:Działa to poprzez wykorzystanie wbudowanych
awk
zmiennych, takich jakNF
(liczba pól w rekordzie), i wykonanie prostejfor
pętli w celu iteracji wzdłuż pól, aby uzyskać te, które chcesz, bez konieczności wcześniejszego wiedzieć, ile będzie.Lub, jeśli naprawdę chcesz po prostu tych konkretnych pól, jak podano w przykładzie:
Jeśli chodzi o pytanie dotyczące wydajności, najprostszą drogą byłoby przetestowanie tej lub każdej z pozostałych metod i użycie,
time
aby pokazać, jak długo to trwa; możesz także użyć narzędzi takich jakstrace
sprawdzenie, jak przebiegają wywołania systemowe. Zastosowanietime
wygląda jak:Możesz porównać te wyniki między różnymi metodami, aby zobaczyć, który jest najbardziej wydajny pod względem czasu; inne narzędzia mogą być wykorzystane do innych wskaźników wydajności.
źródło
echo
vs<<<
„identyczne” to zbyt mocne słowo. Można powiedzieć, żestuff <<< "$list"
jest prawie identyczny zprintf "%s\n" "$list" | stuff
. Jeśli chodzi oecho
vsprintf
, kieruję cię do tej odpowiedzi<<<
dodaje nowy wiersz na końcu. Jest to podobne do sposobu$()
usuwania znaku nowej linii na końcu. Wynika to z faktu, że linie są zakończone przez nowe linie.<<<
podaje wyrażenie jako linię, więc musi być zakończone znakiem nowej linii."$()"
pobiera wiersze i podaje je jako argument, więc warto je przekonwertować, usuwając kończący znak nowej linii.awk
to samodzielny plik binarny, który musi się uruchomić. W przeciwieństwie do Perla, a zwłaszcza Pythona, interpreter awk uruchamia się szybko (wciąż cały zwykły narzut dynamiczny linkera związany z wykonywaniem kilku wywołań systemowych, ale awk używa tylko libc / libm i libdl, np. Służystrace
do sprawdzania wywołań systemowych uruchamiania awk) . Wiele powłok (takich jak bash) działa dość wolno, więc odpalenie jednego procesu awk może być szybsze niż zapętlanie tokenów na liście z wbudowanymi powłokami, nawet dla małych rozmiarów list. I czasami można napisać#!/usr/bin/awk
skrypt, zamiast o#!/bin/sh
skrypcie.Pierwsza zasada optymalizacji oprogramowania: nie .
Dopóki nie dowiesz się, że szybkość programu jest problemem, nie musisz myśleć o jego szybkości. Jeśli twoja lista ma mniej więcej tę długość lub tylko ~ 100-1000 przedmiotów, prawdopodobnie nawet nie zauważysz, ile to zajmie. Istnieje szansa, że poświęcisz więcej czasu na myślenie o optymalizacji, niż jaka byłaby różnica.
Druga zasada: środek .
Jest to pewny sposób, aby się dowiedzieć, i ten, który daje odpowiedzi dla twojego systemu. Zwłaszcza w przypadku muszli jest ich tak wiele i nie wszystkie są identyczne. Odpowiedź na jedną powłokę może nie dotyczyć twojej.
W większych programach profilowanie również tutaj. Najwolniejsza część może nie być taka, jak myślisz.
Po trzecie, pierwsza zasada optymalizacji skryptu powłoki: Nie używaj powłoki .
Tak, naprawdę. Wiele powłok nie jest stworzonych jako szybkie (ponieważ uruchamianie programów zewnętrznych nie musi tak być), a nawet za każdym razem mogą ponownie analizować wiersze kodu źródłowego.
Zamiast tego użyj czegoś takiego jak awk lub Perl. W trywialnym mikro-teście, który zrobiłem,
awk
był dziesiątki razy szybszy niż jakakolwiek zwykła powłoka w uruchamianiu prostej pętli (bez I / O).Jeśli jednak używasz powłoki, użyj wbudowanych funkcji powłoki zamiast poleceń zewnętrznych. Używasz tutaj,
expr
które nie jest wbudowane w żadne powłoki znalezione w moim systemie, ale które można zastąpić standardowym rozszerzeniem arytmetycznym. Np.i=$((i+1))
Zamiasti=$(expr $i + 1)
zwiększaći
. Twoje użyciecut
w ostatnim przykładzie może być również zastąpione standardowymi rozszerzeniami parametrów.Zobacz także: Dlaczego używanie pętli powłoki do przetwarzania tekstu jest uważane za złą praktykę?
Kroki 1 i 2 powinny mieć zastosowanie do twojego pytania.
źródło
awk
pętle są z konieczności lepsze lub gorsze niż pętle powłoki. Chodzi o to, że powłoka jest naprawdę dobra w uruchamianiu poleceń i kierowaniu danych wejściowych i wyjściowych do i z procesów, i szczerze mówiąc, niezgrabna we wszystkim innym; podczas gdy podobne narzędziaawk
są fantastyczne w przetwarzaniu danych tekstowych, ponieważ właśnie po toawk
są tworzone (odpowiednio) powłoki i narzędzia .dash
niż zgawk
, idash
były najszybszą powłoką, którą testowałem ...dash
ibusybox
nie obsługuje(( .. ))
- myślę, że to niestandardowe rozszerzenie.++
jest również wyraźnie wymieniony jako niewymagany, o ile mogę powiedzieć,i=$((i+1))
lub: $(( i += 1))
są bezpieczni.W tej odpowiedzi udzielę jedynie ogólnych wskazówek, a nie punktów odniesienia. Testy porównawcze to jedyny sposób, aby rzetelnie odpowiedzieć na pytania dotyczące wydajności. Ale ponieważ nie mówisz, ile danych manipulujesz i jak często wykonujesz tę operację, nie ma sposobu na wykonanie użytecznego testu porównawczego. Co jest bardziej wydajne dla 10 przedmiotów, a co jest bardziej wydajne dla 1000000 przedmiotów, często nie jest takie samo.
Ogólna zasada polega na tym, że wywoływanie zewnętrznych poleceń jest droższe niż robienie czegoś przy użyciu czystych konstrukcji powłoki, o ile czysty kod powłoki nie wymaga pętli. Z drugiej strony pętla powłoki, która iteruje po dużym łańcuchu lub dużej ilości łańcucha, może być wolniejsza niż jedno wywołanie narzędzia specjalnego. Na przykład, wywoływanie pętli
cut
może być zauważalnie powolne w praktyce, ale jeśli znajdziesz sposób na zrobienie wszystkiego za pomocą pojedynczegocut
wywołania, które prawdopodobnie będzie szybsze niż robienie tego samego z manipulowaniem łańcuchem w powłoce.Należy pamiętać, że punkt odcięcia może się znacznie różnić między systemami. Może zależeć od jądra, od konfiguracji harmonogramu jądra, od systemu plików zawierającego zewnętrzne pliki wykonywalne, od tego, ile procesora w tej chwili naciska pamięć, i od wielu innych czynników.
Nie dzwoń,
expr
aby wykonać arytmetykę, jeśli w ogóle martwisz się wydajnością. W rzeczywistości nie wzywajexpr
do wykonywania arytmetyki. Pociski mają wbudowaną arytmetykę, która jest wyraźniejsza i szybsza niż wywoływanieexpr
.Wygląda na to, że używasz basha, ponieważ używasz konstrukcji bash, które nie istnieją w sh. Dlaczego więc, do cholery, nie miałbyś użyć tablicy? Tablica jest najbardziej naturalnym rozwiązaniem i prawdopodobnie też będzie najszybsza. Zauważ, że indeksy tablic zaczynają się od 0.
Twój skrypt może być szybszy, jeśli używasz sh, jeśli twój system ma kreskę lub ksh
sh
zamiast zamiast bash. Jeśli używasz sh, nie otrzymujesz nazwanych tablic, ale nadal otrzymujesz tablicę jednego z parametrów pozycyjnych, które możesz ustawićset
. Aby uzyskać dostęp do elementu w pozycji, która nie jest znana przed uruchomieniem, musisz użyćeval
(zadbaj o prawidłowe cytowanie rzeczy!).Jeśli kiedykolwiek chcesz uzyskać dostęp do tablicy tylko raz i przechodzisz od lewej do prawej (pomijając niektóre wartości), możesz użyć
shift
zamiast indeksów zmiennych.To, które podejście jest szybsze, zależy od powłoki i liczby elementów.
Inną możliwością jest użycie przetwarzania ciągów. Ma tę zaletę, że nie używa parametrów pozycyjnych, więc możesz użyć ich do czegoś innego. Będzie działać wolniej w przypadku dużych ilości danych, ale jest mało prawdopodobne, aby zauważalna różnica w przypadku małych ilości danych.
źródło
shift && shift && shift
sięshift 3
w trzecim przykładzie - chyba że powłoka używasz nie obsługuje.shift 3
zawiedzie, jeśli pozostanie zbyt mało argumentów. Potrzebujesz czegoś takiegoif [ $# -gt 3 ]; then shift 3; else set --; fi
awk
to świetny wybór, jeśli możesz wykonać całe przetwarzanie w skrypcie Awk. W przeciwnym razie po prostu przesyłasz wyjście Awk do innych narzędzi, niszcząc wzrost wydajnościawk
.bash
iteracja nad tablicą jest również świetna, jeśli zmieścisz całą listę wewnątrz tablicy (co dla nowoczesnych powłok jest prawdopodobnie gwarancją) i nie przeszkadza ci gimnastyka w składni tablicy.Jednak podejście oparte na potoku:
Gdzie:
xargs
grupuje listę oddzieloną spacjami w trzyosobowe partie, każda oddzielona nowym wierszemwhile read
zużywa tę listę i wyświetla pierwszą kolumnę każdej grupygrep
filtruje pierwszą kolumnę (odpowiadającą co trzeciej pozycji na oryginalnej liście)Moim zdaniem poprawia zrozumiałość. Ludzie już wiedzą, co robią te narzędzia, więc łatwo jest czytać od lewej do prawej i rozumieć, co się stanie. Podejście to wyraźnie dokumentuje także długość kroku (
-n3
) i wzorzec filtra (9
), dzięki czemu można łatwo zmieniać:Kiedy zadajemy pytania dotyczące „wydajności”, pamiętaj o „całkowitej wydajności w ciągu całego życia”. Obliczenia te obejmują wysiłek opiekunów, aby utrzymać kod w działaniu, a my, worki mięsne, jesteśmy najmniej wydajnymi maszynami w całej operacji.
źródło
Być może to?
źródło
Nie używaj poleceń powłoki, jeśli chcesz być wydajny. Ogranicz się do potoków, przekierowań, zamian itp. Oraz programów. Właśnie dlatego
xargs
iparallel
narzędzia istnieją - ponieważ bash podczas gdy pętle są nieefektywne i bardzo wolne. Pętli bash należy używać tylko jako ostatniego rozwiązania.Ale powinieneś być nieco szybszy dzięki dobremu
awk
.źródło
Moim zdaniem najczystszym rozwiązaniem (i prawdopodobnie również najbardziej wydajnym) jest użycie zmiennych awk RS i ORS:
źródło
Za pomocą skryptu powłoki GNU
sed
i POSIX :Lub z
bash
„s parametrów podstawienia :Non- GNU ( tj. POSIX )
sed
orazbash
:Lub bardziej przenośnie, używając zarówno POSIX, jak
sed
i skryptu powłoki:Dane wyjściowe któregokolwiek z tych:
źródło