Цикл с более чем одним элементом за раз
Мы можем перебирать множество элементов, считая по одному, например:
#!/bin/bash
for i in $( ls ); do
echo item: $i
done
Как мы можем обрабатывать несколько элементов одновременно в аналогичном цикле? Что-то вроде:
#!/bin/bash
for i,j,k in $( ls ); do
echo item i: $i
echo item j: $j
echo item k: $k
done
Эта вторая оболочка script неверна, но должна дать точную иллюстрацию того, чего я пытаюсь достичь.
Ответы
Ответ 1
Предполагая, что у вас слишком много предметов (хотя оболочка должна иметь возможность
обрабатывать довольно много позиционных аргументов.
# 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[@]}"
Для совместимости с POSIX используйте [ "$#" -gt 0 ]
вместо выражения ((...))
. Нет простого способа сохранить и восстановить все позиционные параметры в совместимом с POSIX образом. (Если нет символа, который вы можете использовать, чтобы объединить их однозначно в одну строку.)
Вот подглавная буква jm666:
(
set -- *
while [ "$#" -gt 0 ]; do
i=$1 j=$2 k=$3
...
shift 3 || shift $#
done
)
Любые изменения в параметрах, которые вы устанавливаете внутри подоболочки, будут потеряны после того, как подоболочка выйдет, но вышеуказанный код в противном случае совместим с POSIX.
Ответ 2
Если имена файлов не содержат пробелов:
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:
Я удалил -print0
и -0
.
Ответ 3
Чтобы получить to get n items a time from the list
, я думаю, вы хотите получить элементы n
из массива.
Используйте его следующим образом:
n=3
arr=(a b c d e)
echo "${arr[@]:0:$n}"
a b c
Ответ 4
Бит позднего ответа, я бы сделал это с не-зрелищным способом:), например:
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)
Ответ 5
Вот другое решение в типичном способе программирования →
#!/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"
Ответ 6
Если у вас есть GNU Parallel, вы можете запустить:
ls | parallel -N3 "echo item i: {1}; echo item j: {2}; echo item k: {3}"
Все новые компьютеры имеют несколько ядер, но большинство программ имеют последовательный характер и поэтому не будут использовать несколько ядер. Однако многие задачи чрезвычайно распараллеливаются:
- Запустите ту же программу во многих файлах
- Запустите ту же программу для каждой строки в файле
- Запустите ту же программу для каждого блока в файле
GNU Parallel - это общий параллелизатор, который упрощает запуск заданий параллельно на одном компьютере или на нескольких компьютерах, к которым у вас есть доступ к ssh.
Если у вас есть 32 разных задания, которые вы хотите запустить на 4-х процессорах, прямой способ распараллеливания - запустить 8 заданий для каждого процессора:
![Simple scheduling]()
GNU Parallel вместо этого запускает новый процесс, когда заканчивается - сохранение активных процессоров и, следовательно, экономия времени:
![GNU Parallel scheduling]()
Установка
Личная установка не требует доступа root. Это можно сделать за 10 секунд, выполнив следующие действия:
(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash
Для других вариантов установки см. http://git.savannah.gnu.org/cgit/parallel.git/tree/README
Подробнее
Дополнительные примеры: http://www.gnu.org/software/parallel/man.html
Смотрите видеоролики: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Пройдите через учебник: http://www.gnu.org/software/parallel/parallel_tutorial.html
Подпишитесь на список адресов электронной почты, чтобы получить поддержку: https://lists.gnu.org/mailman/listinfo/parallel
Ответ 7
Вы можете использовать xargs
, awk
, sed
или paste
для реструктуризации ввода.
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 - - -
}
Тогда любая из комбинаций
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
делает то, что вы хотите.