Если я понял это правильно, я думаю, что вы хотите в основном перебирать списки значений, а затем read
еще один в цикле.
Вот несколько вариантов, 1 и 2, вероятно, самые разумные.
1. Эмулируйте массивы со строками
Было бы неплохо иметь двумерные массивы, но в Bash это невозможно. Если ваши значения не имеют пробелов, используйте один обходной путь, то есть вставьте каждый набор из трех чисел в строку и разбейте строки внутри цикла:
for x in "1 2 3" "4 5 6"; do
read a b c <<< "$x";
read -p "Enter a number: " d
echo "$a - $b - $c - $d ";
done
Конечно, вы можете использовать и другой разделитель, например, for x in 1:2:3 ...
и IFS=: read a b c <<< "$x"
.
2. Замените канал другим перенаправлением, чтобы освободить стандартный ввод.
Другой возможностью является read a b c
чтение с другого fd и направление ввода на него (это должно работать в стандартной оболочке):
while read a b c <&3; do
printf "Enter a number: "
read d
echo "$a - $b - $c - $d ";
done 3<<EOF
1 2 3
4 5 6
EOF
И здесь вы также можете использовать подстановку процесса, если вы хотите получить данные из команды: while read a b c <&3; ...done 3< <(echo $'1 2 3\n4 5 6')
(подстановка процесса - это функция bash / ksh / zsh)
3. Взять пользовательский ввод вместо stderr
Или, наоборот, используя канал, как в вашем примере, но используйте пользовательский ввод read
из stderr
(fd 2) вместо того, stdin
откуда пришел канал:
echo $'1 2 3\n4 5 6' |
while read a b c; do
read -u 2 -p "Enter a number: " d
echo "$a - $b - $c - $d ";
done
Чтение из stderr
немного странно, но на самом деле часто работает в интерактивном сеансе. (Вы также можете явно открыть /dev/tty
, предполагая, что вы хотите фактически обойти любые перенаправления, вот что вроде как less
использует для получения ввода пользователя, даже когда данные передаются на него.)
Хотя stderr
такое использование может работать не во всех случаях, и если вместо него вы используете какую-то внешнюю команду read
, вам, по крайней мере, нужно добавить в команду несколько перенаправлений.
Кроме того, см. Почему моя переменная локальна в одном цикле «пока читается», а не в другом, казалось бы, похожем цикле? для некоторых вопросов, касающихся ... | while
.
4. Нарезать части массива по мере необходимости
Я полагаю, вы также можете аппроксимировать двумерный массив, копируя срезы обычного одномерного:
data=(1 2 3
4 5 6)
n=3
for ((i=0; i < "${#data[@]}"; i += n)); do
a=( "${data[@]:i:n}" )
read -p "Enter a number: " d
echo "${a[0]} - ${a[1]} - ${a[2]} - $d "
done
Вы также можете назначить ${a[0]}
и т. Д. a
, И b
т. Д., Если вы хотите имена для переменных, но Zsh сделает это намного лучше .
stderr
подобное немного странно, но я вспомнил, что какая-то утилита это делает. Но сейчас я могу найти только те, которые просто используют/dev/tty
. Ну что ж.<&2
(а также</dev/tty
) избегайте чтения из stdin скрипта. Это не сработаетprintf '682\n739' | ./script
. Также обратите внимание, чтоread -p
работают только в bash.while
циклу, так что вы не можете использовать stdin скрипта в любом случае ...read -u
это также bash, но его можно заменить перенаправлениями, и<<<
в первый тоже нестандартный, но обойти его немного сложнее.Есть только один
/dev/stdin
,read
будет читать с него везде, где он используется (по умолчанию).Решением является использование некоторого другого файлового дескриптора вместо 1 (
/dev/stdin
).От эквивалента кода (в bash) до того, что вы опубликовали [1] (см. Ниже),
просто добавьте
0</dev/tty
(например), чтобы прочитать из «настоящего» tty:По исполнению:
Другой альтернативой является использование
0<&2
(что может показаться странным, но допустимо).Обратите внимание, что чтение из
/dev/tty
(также0<&2
) будет обходить стандартный сценарий, это не будет читать значения из эха:Другие решения
Что нужно, так это перенаправить один ввод в другой fd (дескриптор файла).
Действительно в ksh, bash и zsh:
Или с помощью exec:
Решение, которое работает в sh (
<<<
не работает):Но это, вероятно, легче понять:
1 более простой код
Ваш код:
Упрощенный код (в bash):
Который, если выполняется, печатает:
Который просто показывает, что var d читается из того же
/dev/stdin
.источник
С помощью
zsh
вы можете написать это вместо:Для 2D-массивов см. Также
ksh93
оболочку:источник