Bash, если условия переменных в пределах интервалов

У меня есть некоторые данные, распределенные по временным интервалам, и я хочу взять некоторые из этих данных за промежутки времени. Например, у меня есть данные по временам в пределах 1..9, 11..19 и т. Д., И я хочу получать данные в пределах 1-2, затем 11-12 и т. Д.

Это будет частью более сложного сценария bash , в котором я хочу включить это условие с циклом if , чтобы изолировать время, в которое я могу поймать данные.

Я думал о чем-то вроде:

 if (( $t_initial & $t_final )) in (begin1, fin1) or in (begin2, fin2) ... 

где t_initial и t_final вычисляются отдельно, самим скриптом.

Я не могу записать это условие в синтаксисе bash . Я нашел некоторые другие решения, но они кажутся очень длинными и неудобными, поэтому я здесь, чтобы попросить более простые и понятные решения.


Важно, чтобы код работал правильно с поплавками. Я пытаюсь исправить решение OP для этого, но все еще не могу найти способ.

4 Solutions collect form web for “Bash, если условия переменных в пределах интервалов”

bash не поддерживает сравнение диапазона или числа с плавающей запятой, поэтому мы должны сделать некоторые из них сами. Я также собираюсь определить функцию и использовать bc для вычисления с плавающей запятой. Вот конечный результат и набор тестов:

 # Call as `compareRanges start end b1 f1 b2 f2 b3 f3...` compareRanges() { local t_initial=$1 local t_final=$2 shift 2 while [ ${#@} -gt 1 ] do local in_range=$(bc <<<"$t_initial >= $1 && $t_final <= $2") if [ $in_range = 1 ] then # Debugging output to stderr - can be removed: echo "[$t_initial,$t_final] is within [$1,$2]" >&2 return 0 fi shift 2 done # Debugging output to stderr - can be removed: echo "[$t_initial,$t_final] is not within any ranges." >&2 return 1 } # Basic integers from the example compareRanges 1 3 2 4 && echo BAD || echo OK compareRanges 1 3 1 3 && echo OK || echo BAD compareRanges 1 3 0 4 && echo OK || echo BAD # Fractional numbers compareRanges 1.5 2.5 1.1 2.2 && echo BAD || echo OK compareRanges 1.5 2.5 0.3 3.1 && echo OK || echo BAD # Multiple ranges compareRanges 5 7 1 4 2 6 3 9 && echo OK || echo BAD compareRanges 5 7 1 2 3 4 5 6 7 8 && echo BAD || echo OK 

Функция compareRanges принимает не менее двух аргументов. Первый – ваш t_initial , а второй – ваш t_final . После этого он может принимать произвольно много других аргументов, которые являются вашими begin1 , fin1 , begin2 , fin2 , по порядку.

Первый тестовый пример сравнивает диапазоны в комментариях к вопросу: 1-3 и 2-4.

 compareRanges 1 3 2 4 && echo BAD || echo OK 

Итак, 1 является t_initial , 3 является t_final , 2begin1 , а 4fin1 .

Когда вы хотите использовать несколько диапазонов, вы выставляете их все в парах:

 compareRanges 5 7 1 4 2 6 3 9 && echo OK || echo BAD 

Здесь мы тестируем 1-4, 2-6 и 3-9. В цикле while мы смотрим на каждую пару по очереди и сравниваем ее с t_initial и t_final .

Поскольку bash не поддерживает дробные числа, мы используем bc , калькулятор произвольной точности. Его ввод задается параметром <<<"$t_initial >= $1" ... : который подает строку в стандартный ввод. $1 – это начало диапазона, который мы сейчас рассматриваем в этой итерации цикла, а $2 – конец; мы сравниваем как нижнюю, так и верхнюю границы одновременно с && . bc будет выводить 1 когда сравнения истинны, и 0 если один из них является ложным. Мы сохраняем результат in_range , и функция выполняется успешно ( return 0 ), когда оба наших теста были истинными.

Дробные числа можно просто указать с их обычной десятичной формой:

 compareRanges 1.5 2.5 0.3 3.1 && echo OK || echo BAD 

bc будет обрабатывать числа с таким количеством дробных цифр, сколько вы хотите, и с любой необходимой величиной.

В конце, если ни одна из граничных пар не соответствует, мы терпим неудачу ( return 1 ). Вы можете использовать эту функцию как:

 if compareRanges $t_initial $t_final 2 4 11 19 then ... fi 

Набор тестов должен печатать все «ОК» при его запуске.


Альтернативно, другие оболочки (такие как zsh ) поддерживают дробные значения переменных. Если вы можете запустить свой скрипт в одном из них, вы можете избежать использования bc , хотя сравнение еще лучше в функции. По крайней мере, в случае zsh они являются поплавками, поэтому они не обязательно точны; bc всегда будет правильным.

Не уверен, хотите ли вы это решение. Он имеет 2 функции:

  • Никаких внешних программ не требуется
  • Он использует функцию, поэтому, по крайней мере, она скрывает сложность сравнений

Это оно:

 #!/bin/bash # The comparing function function compareInterval { t1=$1 t2=$2 shift 2 while (( "$2" )); do if (( $t1 >= $1 && $t2 <= $2 )); then # got match return 0 fi shift 2 done return 1 } # sample values t_initial=2 t_final=4 # Invocation. Compares against 1-3, 3-5, 2-5 if compareInterval $t_initial $t_final 1 3 3 5 2 5; then echo Got match fi 

Вот пара бесстыдных разрывов ответа LatinSuD, которые обрабатывают с плавающей запятой. Вы заметите, что его ответ может похвастаться «Никаких внешних программ не требуется». В этой программе используется калькулятор bc , как он предложил:

 #!/bin/bash # The comparing function function compareInterval { t1=$1 t2=$2 shift 2 while (( "$2" )) do # if (( $t1 >= $1 && $t2 <= $2 )) bc_result=$(echo "print $t1 >= $1 && $t2 <= $2" | bc) if [ "$bc_result" = 1 ] then # got match return 0 fi shift 2 done return 1 } # sample values t_initial=2.3 t_final=4.2 # Invocation. Compares against 1-3, 3-5, 2-5 if compareInterval $t_initial $t_final 1 3 3 5 2 5 then echo Got match fi 

Это просто принимает значение if (( $t1 >= $1 && $t2 <= $2 )) и отправляет его на bc , а затем фиксирует вывод из bc .


Другим подходом является нормализация чисел до целых чисел путем умножения на десять. Это требует, чтобы у вас было максимальное число десятичных цифр. Например, если ни одна точка данных не имеет более трех цифр справа от десятичной точки, мы можем умножить все на 1000.

 #!/bin/bash # Normalize function: it multiplies a floating point number by 1000 # without using floating point arithmetic. normalize() { case "$1" in *.*) result=$(echo "$1"000 | sed 's/\(.*\)\.\(...\).*/\1\2/') ;; *) result="$1"000 esac echo "$result" } # The comparing function function compareInterval { t1=$(normalize $1) t2=$(normalize $2) shift 2 while (( "$2" )) do a1=$(normalize $1) a2=$(normalize $2) if (( $t1 >= $a1 && $t2 <= $a2 )) then # got match return 0 fi shift 2 done return 1 } # sample values t_initial=2.3 t_final=4.2 # Invocation. Compares against 1-3, 3-5, 2-5 if compareInterval $t_initial $t_final 1 3 3 5 2 5 then echo Got match fi 

Если параметр для функции normalize является простым целым числом (т. Е. Числом без десятичной точки, например, 17 ), мы можем умножить на 1000 просто путем добавления 000 , поэтому 1717000 . Если параметр для normalize является числом с плавающей запятой (т. Е. Содержит десятичную точку, например, 42.5 ), мы все равно добавляем 000 , а затем используем sed для удаления десятичной точки и всего после третьей цифры. Команда sed s/\(.*\)\.\(...\).*/\1\2/ принимает строку типа abcdef . ghijkl и возвращает abcdefghi , поэтому 42.542.500042500 (т. е. 42,5 × 1000).

Возможно, это можно сделать с помощью строковых манипуляций в bash без использования sed .

Вещь «11..19», о которой вы просите, называется расширением скобки.

Вы можете использовать либо eval {$t_initial..$t_final} ,

…или

if `seq $t_initial..$t_final`==$somevalue

  • Арифметическое расширение Shell с котировками
  • Is> & - эффективнее, чем> / dev / null?
  • Как добавить данные о подключении к сети или пользователям в записи истории?
  • Возможно ли сделать переменную внутри вложенной для петлевого локального
  • Укажите тип поля для изменения в файле
  • Выяснить, что такое sh
  • Как псевдоним cp с cp -i по умолчанию
  • Как поиск в $ PATH работает под капотом?
  • Игнорировать глобусы, которые не соответствуют чему-либо
  • Дублировать файл x раз в командной оболочке
  • Как работает четкая команда?
  • Linux и Unix - лучшая ОС в мире.