2014-09-12 14 views
11

Możemy iteracyjne nad zbiór elementów, uznając po jednym na raz, tak:pętla z więcej niż jednego elementu naraz

#!/bin/bash 
for i in $(ls); do 
    echo item: $i 
done 

Jak możemy przetwarzać kilka przedmiotów naraz podobnej pętli ? Coś takiego:

#!/bin/bash 
for i,j,k in $(ls); do 
    echo item i: $i 
    echo item j: $j 
    echo item k: $k 
done 

Ten drugi skrypt powłoki jest nieprawidłowy, ale powinien dać dokładną ilustrację tego, co próbuję osiągnąć.

+3

Nie używaj 'ls' tak: używać' for i in *; zamiast tego. – chepner

Odpowiedz

1

Aby uzyskać to get n items a time from the list Myślę, że chcesz uzyskać n przedmiotów z tablicy.

Używaj go tak:

n=3 
arr=(a b c d e) 

echo "${arr[@]:0:$n}" 
a b c 
3

Jeśli nazwy plików nie zawiera spacji:

find . -maxdepth 1 | xargs -L 3 | while read i j k; do 
    echo item i: $i 
    echo item j: $j 
    echo item k: $k 
done 

EDIT:

usunąłem -print0 i -0.

+0

Właściwie, aby być * absolutnie * bezpiecznym, potrzebujesz '--print0', a także * 3 * poprawnie skonstruowanych wywołań' odczytania'. – chepner

+1

Myślę, że opcje '-print0' i' -0' mogą pomóc w nazwach plików zawierających białe spacje. – SzG

+1

@SzG, ale tylko wtedy, gdy przeczytany odczyt ma ich wartość "null". W przeciwnym razie nie ma sensu. I nie można odczytać (z 'read') wartość null została zakończona podczas używania' xargs' ... Zatem '-print0' i właściwe' read' lub '-print | xargs' ... nie oba. – jm666

4

Zakładając, że nie mają zbyt wiele elementów (choć powłoka powinna być w stanie uchwytem sporo argumentów pozycyjnych.

# Save the original positional arguments, if you need them 
original_pp=("[email protected]") 
set -- * 
while (($# > 0)); do 
    i=$1 j=$2 k=$3  # Optional; you can use $1, $2, $3 directly 
    ... 
    shift 3 || shift $# # In case there are fewer than 3 arguments left 
done 

# Restore positional arguments, if necessary/desired 
set -- "${original_pp[@]}" 

Dla zgodności z POSIX, zamiast używać [ "$#" -gt 0 ] wyrażenia ((...)). Nie ma łatwego sposobu na zapisanie i przywrócenie wszystkich parametrów pozycyjnych w sposób zgodny z POSIX (chyba że istnieje znak, którego można użyć do jednoznacznego połączenia w jeden ciąg znaków).

Oto podpowłokę jm 666 wspomina:

(
    set -- * 
    while [ "$#" -gt 0 ]; do 
     i=$1 j=$2 k=$3 
     ... 
     shift 3 || shift $# 
    done 
) 

Wszelkie zmiany parametrów ustawione wewnątrz podpowłoce zostaną utracone po wyjściach podpowłoki, ale powyższy kod jest inaczej POSIX-kompatybilne.

+2

Można użyć podpowłoki, o ile nie trzeba ustawiać żadnych parametrów, które będą potrzebne później. – chepner

-1

Możesz użyć xargs, awk, sed lub paste, aby zmienić swoje dane wejściowe.

job_select() 
{ 
    ls 
} 

job_process() 
{ 
    while read i j k; do 
    printf 'item i: %s\nitem j: %s\nitem k: %s\n' "$i" "$j" "$k" 
    done 
} 

job_restructure_xargs() 
{ 
    xargs -L 3 
} 

job_rstructure_awk() 
{ 
    awk '(NR % 3 == 1) { i = $0 } (NR % 3 == 2) { j = $0 } (NR % 3 == 0){ k = $0; print(i,j,k)}' 
} 

job_restructure_sed() 
{ 
    sed -e 'N;N;s/\n/ /g' 
} 

job_restructure_paste() 
{ 
    paste - - - 
} 

Następnie każdy z kombinacji

job_select | job_restructure_xargs | job_process 
job_select | job_restructure_awk | job_process 
job_select | job_restructure_sed | job_process 
job_select | job_restructure_paste | job_process 

robi to, co chcesz.

+1

Nie parsuj danych wyjściowych 'ls', szczególnie nie w takiej strukturze Rube-Goldberg-esque. To nie będzie działać dla nazw plików zawierających białe spacje. – chepner

+0

Po prostu założyłem, że tak bezpieczne było parsowanie wyjścia 'ls', jak je przyjmował OP. W programowaniu powłoki, przetwarzanie danych odbywa się za pomocą filtrów, więc nie ma tu nic takiego jak Rube-Goldberg, tylko zwykłe programowanie powłoki. To powiedziawszy, twoja odpowiedź jest o wiele lepsza! –

+0

Odbieram komentarz Rube Goldberga; Nie wyglądałem wystarczająco dokładnie i nie zauważyłem, że trzy funkcje 'job_restructure_ *' to tylko 3 alternatywne implementacje tego samego zadania. Jednak najlepiej jest zgnieść chęć parsowania 'ls', gdziekolwiek się pojawi. – chepner

1

Bit późno odpowiedź, chciałbym to zrobić bez spektakularny sposób :), jak:

while read -r -d $'\0' f1 
do 
     read -r -d $'\0' f2 
     read -r -d $'\0' f3 

     echo "==$f1==$f2==$f3==" 

done < <(find test/ ... findargs... -print0) 
0

Oto inne rozwiązanie w typowy sposób programowania ->

#!/bin/bash 
shopt -s nullglob 
arr=(*) #read the files/dirs in an array 
total=${#arr[@]} #get the array size 
count=0; 
#loop it thru in multiples of three 
while [ $count -lt $((total-2)) ] 
do 
     echo "i is ${arr[$count]}" 
     echo "j is ${arr[$((count+1))]}" 
     echo "k is ${arr[$((count+2))]}" 
     count=$((count+3)) 
done 
#print the remaining element(s) 
rem=$((total%3));  
if [ $rem -eq 1 ]; 
then 
     echo "i is ${arr[$total-1]}" 
elif [ $rem -eq 2 ]; 
then 
     echo "i is ${arr[$total-2]}" 
     echo "j is ${arr[$total-1]}" 
fi 
echo "Done" 
0

Jeśli GNU Równolegle można uruchomić:

ls | parallel -N3 "echo item i: {1}; echo item j: {2}; echo item k: {3}" 

Wszystkie nowe komputery mają wiele rdzeni, ale większość programów jest szeregowych w i dlatego nie użyje wielu rdzeni. Jednak wiele zadań są bardzo parallelizeable:

  • Uruchom ten sam program na wielu plikach
  • ten sam program dla każdej linii w pliku
  • ten sam program dla każdego bloku w pliku

GNU Parallel to ogólny paralelizator, który umożliwia łatwe wykonywanie zadań równolegle na tym samym komputerze lub na wielu komputerach, do których masz dostęp ssh.

Jeśli masz 32 różnych zadań, które mają być uruchamiane na 4 procesorów, prosto do przodu sposobem parallelize jest uruchomienie 8 zadań na każdym CPU:

Simple scheduling

GNU Parallel zamiast spawns nowy proces, gdy jeden kończy - utrzymanie procesorów aktywny i oszczędzając czas:

GNU Parallel scheduling

Instalacja

Instalacja osobista nie wymaga uprawnień administratora. Można to zrobić w ciągu 10 sekund w ten sposób:

(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash 

innych opcji instalacyjnych zobaczyć http://git.savannah.gnu.org/cgit/parallel.git/tree/README

Więcej

Zobacz więcej przykładów: http://www.gnu.org/software/parallel/man.html

oglądać Wstep filmy: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Spacer t on Tutorial: http://www.gnu.org/software/parallel/parallel_tutorial.html

zapisać się do listy e-mail, aby uzyskać wsparcie: https://lists.gnu.org/mailman/listinfo/parallel

+0

Nie sądzę, że uruchamianie losowego kodu z Internetu (nawet bardziej, jeśli jest to zwykły http, nie https) jest dobrą radą. (I tak, mówię o tym "instalatorze", a nie o zainstalowanym oprogramowaniu). –

+0

To jest na ogół dobra rada: Lepiej jest instalować ścieżkę zabezpieczoną cyfrowymi podpisami (np. Apt). Ale niektórzy ludzie nie mają tego luksusu: może nie mają dostępu do roota, może używają dystrybucji, dla której GNU Parallel nie jest zapakowany. Dla tych osób nie będzie żadnych różnic pod względem bezpieczeństwa, niezależnie od tego, czy uruchamiają instalator bezpośrednio z Internetu, czy też uruchamiają zainstalowany program: każdy z nich może zawierać backdoor. –

+0

Przynajmniej podaj adres URL https dla instalatora, więc jeśli (słusznie) ufasz swojej witrynie (i jej certyfikatowi), nie otrzymają wersji, która zostanie zastąpiona przez agresora MitM. –

Powiązane problemy