LINUX.ORG.RU

Обработка содержимого переменных с составными именами.


0

1

Имеем два одинаковых по размеру и формату записи (форма: «VAR_Digit=VAL»; при этом «VAR» принимает только два значения: в одном документе «new», в другом - «old» соответственно, а «Digit» бывает разным, но в обоих списках одинаковое, то есть для каждого «new_XXX=...» найдётся «old_XXX=...», но с другим значением - «new_1=875» соответствует «old_1=674»).

Проблема: хотя списки и соответствуют друг другу по количеству строк, числа у имён переменных остаются рандомными, а число строк заранее не известно.

Решение первого шага: для того, чтобы не маяться с неопределяемыми заранее диапазонами чисел воспользовался выборкой из одного списка:

cat "list1" | grep new_ | sed 's/new\(_[0-9]\{1,2\}\)=[0-9]\{1,9\}/\1/g'
Получил список вида «_XXX», где ХХХ - число.

Задача: Сравнивать значения «old_XXX=...» и «new_XXX=...». Если значение «новой» переменной отличается от старой «старой» - напечатать этот диапазон:

seq ${old_XXX} ${new_XXX}

Решение второго шага: Пытался собрать обработчик вида

fi [ {old}_{digit} < {new}_{digit} ] ; then .... ; fi
, но это не заработало. Единственное, что смог найти - решение через «READ», но мне не нужно вводить это вручную! Как автоматизировать?

Решение через read

read ${переменная 1}${переменная 2}
 
echo ${значение переменной1 и 2 } // ввод без пробелов слитно  одним  текстом.
 
// выводит  значение , значения переменной 1 и 2. Переменная  не должна  начинаться с цифры и тд.
 
g=a1
h=a2
read ${g}${h}
# Text
echo ${a1a2}
Text

★★

Что-нибудь вроде: comm -13 <(cut -d_ -f2- old) <(cut -d_ -f2- new) | cut -d= -f1

// если строки не отсортированы - добавить sort после cut, и <(...) - башизм

anonymous
()

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

Если он есть переменная будет считаться строковой и сравнение не будет работать.

Если вывод примерно такой:

declare -p old_XXX
old_XXX=1234\r
то сравнение не будет работать, с помощью 'sed' или 'tr' убирайте спецсимвол, 'tr' предпочтительнее.

Да и операции сравнения лучше использовать эти:

man test
...
INTEGER1 -eq INTEGER2
              INTEGER1 is equal to INTEGER2

       INTEGER1 -ge INTEGER2
              INTEGER1 is greater than or equal to INTEGER2

       INTEGER1 -gt INTEGER2
              INTEGER1 is greater than INTEGER2

       INTEGER1 -le INTEGER2
              INTEGER1 is less than or equal to INTEGER2
...

kostik87 ★★★★★
()

Единственное, что смог найти - решение через «READ», но мне не нужно вводить это вручную! Как автоматизировать?

Имеете в виду это?
while read foo bar; do echo $foo; echo $bar; done < file
(или иным способом - тем же пайпом - накормить STDIN)

grep new_ | sed 's/new\(_[0-9]\{1,2\}\)=[0-9]\{1,9\}/\1/g'

sed -rn '/new_/s/new(_[0-9]{1,2})=[0-9]{1,9}/\1/gp'

Не проще ли это написать на Perl?


use warnings;
use strict;
use feature 'say';
die unless @ARGV == 2;
open my $ofh,«<»,$ARGV[0] || die «$ARGV[0]: $!»;
open my $nfh,«<»,$ARGV[1] || die «$ARGV[1]: $!»;
my %old = map { $1 => $2 if /^VAR_(\d+)=(\d+)/ } <$ofh>;
my %new = map { $1 => $2 if /^VAR_(\d+)=(\d+)/ } <$nfh>;
for (keys %new) {
if ($old{$_} != $new{$_}) {
my ($min,$max) = sort { $a <=> $b } ($old{$_},$new{$_});
say for ($min..$max);
}
}


Код не проверял, не укорачивал (эти хэши и хэндлы хорошо бы в массив запихнуть и пару циклов сделать), perl -c говорит, что syntax OK.

AITap ★★★★★
()
Ответ на: комментарий от AITap

Спасибо за перл, но тут два препятствия: я знаю только BASH/SHELL, и я не знаю Perl.

Благодарю за трюк с SED`ом - я такого не знал, да и идея с циклом мне понравилась, хотя я до неё сам как-то не додумался...

zzdnx ★★
() автор топика
Ответ на: комментарий от AITap

О PREL

Кстати, в той системе, где это будет работать нет перла ;-( И поставить мне его не позволят...

zzdnx ★★
() автор топика
Ответ на: комментарий от AITap

Общая задача выглядит так: есть 2 текстовых документа со сходным содержанием.

Задача: в скрипте есть функция, которой в качестве входного параметра нужен диапазон разницы «старого» и «нового» чисел, взятых из внешних источников; то есть, если в файле 1 записано «old_4=17», а в файле 2 написано «new_4=20» - я должен передать в функцию такой параметр: «18 19 20». И так для каждой пары «old/new».

Я подумал что самый простой метод решить это башем - либо массивы, либо составное имя переменной, что и пытаюсь реализовать. Если есть какой-то ещё способ решения - предлагайте. Буду очень признателен.

zzdnx ★★
() автор топика
Ответ на: комментарий от zzdnx

Составное имя переменной делать не стоит, это потребует eval и прочие опасные вещи.
Попробуйте массивы:

# сортируем "новый файл", регуляркой вырезаем индексы и запихиваем в массив "index"
index=(sort -n newfile | sed -rn 's/VAR_([0-9]+)=[0-9]+/\1/')
# каждый из файлов снова сортируем, регуляркой вырезаем и индекс, и значение, и запихиваем в массивы "old" и "new"
sort -n oldfile | sed -rn -e 's/VAR_([0-9]+)=([0-9]+)/\1 \2/p' | while read idx val; do old[$idx]=$val; done
sort -n newfile | sed -rn -e 's/VAR_([0-9]+)=([0-9]+)/\1 \2/p' | while read idx val; do new[$idx]=$val; done
for ind in ${index[@]} # перебирая все найденные индексы,
do
 if [ ${old[$ind]} -lt ${new[$ind]} ]; then # сравниваем значения
  function $(seq ${old[$ind]} ${old[$ind]})
 elif [ ${old[$ind]} -gt ${new[$ind]} ]; then # по данным индексам
  function $(seq ${new[$ind]} ${old[$ind]})
 fi
done

AITap ★★★★★
()

Perl же, ну. Если что, бывает portable strawberry perl. Он сюда прямо-таки просится. Как решить на bash, даже и не знаю.

Hoodoo ★★★★★
()
Ответ на: комментарий от AITap

Благодарю за код с массивами!

Однако он не очень хорошо работает. Участок кода «while read idx val» назначает соответствующие переменные «old[$idx]=$val», но они доступны и действительны только до конца цикла. То есть после «done» вызов «echo old[$idx]» вернёт пустую строку. Если бы ни это, то код был-бы идеален. Ищу способ устранения этого бага...

zzdnx ★★
() автор топика
Ответ на: Благодарю за код с массивами! от zzdnx

Переменные вне while не сохраняются, поскольку while - это отдельный процесс. А зачем нужно значение по последнему индексу?

У Вас есть все индексы в отдельном массиве, так что попробуйте echo ${old[${index[${#index[@]}]}]} - «старое» значение, взятое по последнему из найденных индексов. Знаю, выглядит ужасно.

AITap ★★★★★
()
Ответ на: комментарий от AITap

echo ${old[${index[${#index[@]}]}]}

Справился с многомерностью массива, но вывод этого выражения оказался пустым ;-(

zzdnx ★★
() автор топика
Ответ на: комментарий от AITap

echo ${#index[@]} возвращает «1», остальные две пусты независимо от положения этого дебага. Переменные old и new оказываются пустыми сразу же после выхода из цикла, который назначается while`ом. Вообще есть идея - назначить эти два массива (старые и новые данные) ДО того, как вызывать «while read», который изменит только нужные элементы выбраного массива, однако есть вероятность что все изменения сделанные циклом «while read» будут принадлежать его локальной среде, а, следовательно, эти изменения не выйдут за рамки своего цикла. Эту идею ещё не проверял - спать, больно, охота... Проверю и отпишусь завтра.

zzdnx ★★
() автор топика
Ответ на: комментарий от AITap

Решение через массивы

Так и есть - при выходе из цикла умирают и все его локальные изменения. Вышел из ситуации методом наращивания уровня вложенности:

1) Выборка индексов

2) Цикл 1: grep`аем строку с нужным индексом из документа «old» и создаём переменную old[$idx]=$val

3) Цикл 2 (внутри цикла №1): повторяем то же самое для документа «new».

4) Внутри цикла №2 - проверяем условие и делаем вывод требуемой информации.

Ясен фиг что циклы используются не по максимуму, но другого варианта пока в голову не пришло. Большое спасибо за помощь и тему можно закрывать.

Ещё благодарю anonymous`а за сведенья о соменде comm (я о ней не знал даже) и пользователя kostik87 за очень интересные сведенья касающиеся типов переменных. Мне стали понятны ранее загадочные ошибки нескольких скриптов.

zzdnx ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.