Как мы можем использовать несколько переменных в одном цикле for в сценарии оболочки?

6

Я реализую как код ниже, используя для цикла, но неправильный вывод после запуска сценария.

for i in `awk -F"|" '{print $1}' $INPUTFILE`, j in `awk -F"|" '{print $2}' $INPUTFILE`
do
echo $i:$j
done  

Пожалуйста, помогите мне использовать несколько переменных в одном цикле for в скрипте оболочки.

user3548033
источник
Возможно связано: Передача нескольких параметров через xargs . Смотрите, в частности , ответ Стефана Шазела .
G-Man

Ответы:

10

bash допускает циклы с более чем одной переменной, но только в C, как синтаксис:

for ((i=0,j=10;i<=j;i++,j--))
do
   echo "i=$i"
   echo "j=$j"
done
Dani_l
источник
как я и j читаем из $ INPUTFILE?
Archemar
@ Archemar Это не так. Вопрос, я считаю, неправильный. Использование циклов for в awk, как правило, неправильно, так как вы можете выполнить все, что хотели в чистом awk, и посмотрите ответ Стефана Шазеласа. Я только что ответил на вопрос в названии.
Dani_l
8

Может быть, вы действительно хотите:

while IFS='|' read -r i j rest <&3; do
  {
    printf '%s\n' "something with $i and $j"
  } 3<&-
done 3< "$INPUTFILE"

Но использование цикла оболочки для обработки текста часто является неправильным способом .

Здесь звучит так, будто вам просто нужно:

awk -F '|' '{print $1 ":" $2}' < "$INPUTFILE"

Теперь, как ответ на вопрос в заголовке, для оболочки с forциклами, принимающими более одной переменной, вы получили zsh(кажется, что вы уже используете синтаксис zsh, не заключая в кавычки свои переменные или не отключая глобализацию при разбиении подстановки команд):

$ for i j in {1..6}; do echo $i:$j; done
1:2
3:4
5:6

Или более короткая форма:

for i j ({1..6}) echo $i:$j

Эквивалент с оболочками POSIX:

set -- 1 2 3 4 5 6
## or:
# IFS='
# ' # split on newline
# set -f # disable globbing
# set -- $(awk ...) # split the output of awk or other command
while [ "$#" -gt 0 ]; do
  echo "$1:$2"
  shift 2
done
Стефан Шазелас
источник
Есть ли способ сделать это без использования нового fd ( <&3) и что -после этого делать? Просить из любопытства.
sdkks
@sdkks, вы можете использовать любой fd, какой захотите (от 0 до 9, некоторые оболочки позволяют больше, некоторые даже позволяют динамически их распределять), но лучше избегать 0, 1 и 2, поскольку они особенные. Это stdin, stdout и stderr, и команды внутри цикла, возможно, должны использовать их (в случае printfтолько 1 и, возможно, 2 для ошибок). 3<&-это синтаксис для закрытия FD. Мы закрываем 3 для команд внутри цикла, поскольку они не нуждаются в доступе (и не должны использовать) к этому ресурсу.
Стефан
0

Вам понадобится несколько циклов, если вы хотите перебрать более одной переменной, например for i in 1 2 3; do for j in a b c; do ...; done; done. когда список вещей для перебора является выходом другой команды, вам нужно обернуть эту другую команду $().

Хенрик
источник
Я не уверен, что OP хочет перекрестный продукт, более вероятно, что OP хочет i, j из первой строки $ INPUTFILE, затем i, j из второй строки и так далее.
Archemar
Я решил не пытаться разобрать части вопроса на awk, но после перечитывания, я думаю, вы правы, и ответ Стефана прав.
Хенрик
0

Вы можете сохранить вывод файла в переменной, а затем разбить его на две части. пример:

в примере содержимое файла:

a:1
b:2
c:3
d:4

сейчас скрипт.

#!/bin/bash
for n in $(cat example)
do
    first=$(cat $n | cut -d ":" -f 1)
    second=$(cat $n | cut -d ":" -f 2)
    echo $first
    echo $second
done
Бикаш Шоу
источник
0

На встраиваемых системах вы часто наносите ущерб тому, что доступно. НАПРИМЕР. forможет иметь только одну переменную; awk& printfпросто не доступны; или IFSне поддерживается; и т.п.

В этих условиях вы можете достичь своей цели следующим образом:

export i=xxx
for j in `cat INPUTFILE` ; do \
    [ $i == xxx ] && export i=$j && continue ; \
    echo "$i:$j" ; \
    export i=xxx ; \
done
unset i

Более общая форма этого решения может выглядеть так:

export A=xxx
export B=xxx
for i in 1 2 3 4 5 6 7 8 9 10 11 ; do \
    [ $A == xxx ] && export A=$i && continue ; \
    [ $B == xxx ] && export B=$i && continue ; \
    echo "$A:$B:$i" ; \
    export A=xxx ; \
    export B=xxx ; \
done
unset A
unset B
  • Предполагается, что строка «xxx» никогда не появляется в вашем наборе данных ... Если это так или может, выберите что-то еще.
  • Вы можете иметь любое количество «полей» на «запись» ... Просто продублируйте каждую из ЧЕТЫРЕХ строк, которые обрабатывают поля {A, B, ...}.
  • Последнее поле всегда называется $ i ... Не стесняйтесь добавлять export C=$iпрямо перед тем, echoесли это поможет.
  • Если количество строк в наборе данных не кратно размеру записи, «остаток» (т. Е. | Set |% | record |) будет потерян.
Голубых фишек
источник
1
Я понятия не имею, что вы говорите. Пожалуйста, объясните (1) что вы думаете о вопросе (то есть, на какой вопрос вы отвечаете), (2) что вы думаете во входном файле и (3) какие результаты вы пытаетесь получить. Пожалуйста, не отвечайте в комментариях; отредактируйте  свой ответ, чтобы сделать его более понятным и полным.
G-Man
(1) Я думаю, что вопрос задан в верхней части страницы. (2) Я думаю, что содержание входного файла определяется [неопределенно подразумевается] вопросом. (3) Я успешно создаю функциональность, необходимую для вопроса [последней строки] ... Информация в этом комментарии не относится к ответу.
BlueChip
0

GNU Parallel часто может быть решением для этого:

parallel -a $INPUTFILE --colsep '|' echo {1}:{2}
Оле Танге
источник