Это определенно возможно, но неприятно. На самом деле, вы хотели бы пойти по-другому в отношении вещей.
Для этих конкретных файлов и этой задачи этот сценарий, вероятно, является самым простым и использует один цикл while
вместо любогоfor
:
цикла.
exec 3
Он имеет фиксированную структуру из трех -столбцов для каждого файла и просто считывает их все сразу. file2
открывается в файловом дескрипторе 3 (exec 3
), так что вы можете читать его независимо от того,file1
:то, что вы написали, открывает файл и читает первую строку только каждый раз. Команды read
помещают первое слово в x1
, второе в x2
и остальную часть строки в `x3.
Нет встроенного -или достаточно простого способа "сжать" два списка вместе или написать параллельный цикл for. Циклы for для чего-либо, кроме простых массивов или списков буквальных слов, сложны или невозможны, и создание правильного массива требует больше усилий, чем выполнение задачи другим способом.
Если у вас есть переменное количество столбцов, это сложнее, но мы можем использоватьmapfile
для создания массивов из строк, а затем обычную обработку массива с помощью цикла C -стиля for
для каждой строки:
while read line1 && read line2 <&3
do
mapfile -d $'\t' a1 <<<"$line1"
mapfile -d $'\t' a2 <<<"$line2"
for ((i=0; i<${#a1[@]}; i++))
do
echo $((a1[i] - a2[i]))
done
done
При этом создаются два массива a1
и a2
, содержащие вкладку -, разделенные элементами каждой строки , и выполняется цикл до длины строки из file1
(, игнорируя любые дополнительные элементы из другого файла ).
Наилучшее обобщенное приближение чего-то вроде zip
, предполагая, что файлы правильно -сформированы и количество столбцов совпадает в соответствующих строках,будет что-то вроде while read a b... done < <(paste <(printf '%s\n' $(
Тем не менее, сценарии оболочки не являются хорошим механизмом для такого рода обработки , и awk -или даже лучше, правильный язык -был бы гораздо более подходящим и менее хрупким, чем этот.
Я думаю, вы можете видеть из вышеприведенных скриптов, что это слишком сложно для достижения, довольно хрупко и трудно следовать, потому что язык просто не создан для этого. Эти сценарии более читабельны, чем другие способы добиться того же результата, и это не о многом говорит.
Немного запутанно, но можно:
yes 'date -Is' | pv -qlL1 | sh
(также имеет то преимущество, что команда выполняется каждую секунду, а не каждые 2 секунды плюс время, необходимое для ее выполнения)
1
выше — скорость в количестве строк в секунду и может быть только целым числом. Таким образом, 1 в секунду - это самое медленное, что вы можете получить. Вы можете отказаться от -l
, хотя в этом случае скорость будет в количестве байтов в секунду. Таким образом, при 9 байтах на строку это будет одна команда каждые 9 секунд.
perl -e '$t=shift; while(1){print $o=$n if ($n=qx(@ARGV)) ne $o; sleep $t}' 1 date -Is
2020-10-07T06:49:07+03:00
2020-10-07T06:49:08+03:00
2020-10-07T06:49:09+03:00
2020-10-07T06:49:10+03:00
Улучшенная версия, которая выполняет дробные задержки и правильно обрабатывает пробелы и специальные символы в команде и аргументах:
#! /usr/bin/perl
use Time::HiRes qw(sleep);
die "usage: $0 delay cmd [args...]\n" if @ARGV < 2;
sub slurpcmd {
open my $h, '-|', @_ or die "open $_[0]|: $!";
local $/ unless wantarray;
<$h>
}
my $t = shift; my ($o, $n);
while(1){print $o=$n if ($n=slurpcmd @ARGV) ne $o; sleep $t}
Добавление опций для форматирования вывода команды (красивое разделение, если многострочный и т. д. )оставлено читателю в качестве упражнения;-)