Цикл с более чем одним элементом за раз

Мы можем перебирать множество элементов, считая по одному, например:

#!/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

делает то, что вы хотите.