LINUX.ORG.RU

BASH оптимальный вариант для решения задачи

 , ,


0

1

Есть файл в котором 500тыс. строк, строки состоят из двоичного кода. Нужно: если мы встречаем первыми двумя символами «11», то это означает что число отрицательное, далее отнимаем первые 2 символа от строки, а остальные инвертируем, т.е. 1 стал 0 и наоборот, затем переводим получившуюся строку в десятичное число и записываем в файл со знаком «-»; если мы встречаем первыми символами «00», «01» или «10», то это означает что число положительное, соответственно также отнимаем первые 2 символа, переводим в десятичное и записываем в файл. Скрипт работает, но считает очень долго, испробовал множество способов, как сам считаю оптимизировал по максимуму(я в BASH относительно новичок). ПОМОГИТЕ ускорить процесс….

while read Line
do
a=11
if [[ "${Line:0:2}" == "$a" ]]
then
echo "-"$((2#$(echo ${Line:2:14} | sed -e 's/0/w/g; s/1/q/g' | sed -e 's/w/1/g; s/q/0/g')))
else
echo $((2#${Line:2:14}))
fi
done <file.txt


Последнее исправление: max27_09 (всего исправлений: 1)

Как минимум сократи число конвейеров и лишних вызовов команд

echo "-"$((2#$(echo ${Line:2:14} | sed -e 's/0/w/g; s/1/q/g' -e 's/w/1/g; s/q/0/g')))

Перепиши на компилируемом языке c/c++.

kostik87 ★★★★★
()
Последнее исправление: kostik87 (всего исправлений: 1)

Есть золотое правило: «ничего лишнего в цикле». Справедливо для любого языка программирования.

Есть абсолютно «новичковая» ошибка: «a=11». Абсолютно нечего ей делать внутри цикла.

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

там где надо инвертировать (4095 – для 12бит)

echo $((2#$(echo ${Line:2:14}))) | awk '{print xor($1, 4095)}'

можно так записать

awk '{print xor($1, 4095)}' <<< $((2#$(echo ${Line:2:14})))
futurama ★★★★★
()
Последнее исправление: futurama (всего исправлений: 3)
Ответ на: комментарий от max27_09

Что именно не работает? У тебя числа 12битные (после отрезания 2х знаковых битов)


# echo $((2#$(echo ${Line:2:14}))) | awk '{print xor($1, 4095)}' 
  echo $((2#${Line:2:14}))         | awk '{print xor($1, 4095)}'
futurama ★★★★★
()
Последнее исправление: futurama (всего исправлений: 2)
x=6144 #b1100000000000
m=2047 #  b11111111111
while read Line; do
  num=$((2#$Line))
  if [[ "$num" -ge $x ]]; then
    num=$(($num-$x))
    echo "-"$(( $num ^ $m ))
  else
    echo $((2#${Line:2:14}))
  fi
done <file.txt

Результат

$ time ./aaa.sh | wc -l
500000

real	0m6.444s
user	0m5.619s
sys	0m2.023s
futurama ★★★★★
()
Последнее исправление: futurama (всего исправлений: 6)
Ответ на: комментарий от futurama

Мне все-равно, это просто для сравнения

Раз уж у вас есть большой тестовый файл, всегда было интересно, будет ли быстрее если писать красиво - убрать совсем не нужные $ и тем более кавычки в [[ ]] и в (( ))

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

я уже удалил файл, но вот он (работает долго, лучше ограничиться 50к и потом cat его 10 раз)

for ((i=0; i<500000; i++)); do
  a=`echo "obase=2; $(($RANDOM % 8192))" | bc`;
  echo "000000000000$a" | sed -r 's/.*(.{13})$/\1/' >> bin_500k.txt
done

красиво это как-то так?

let x=2#1100000000000
let m=2#0011111111111

while read Line; do
  let num=2#$Line
  if [[ num -ge x ]]; then
    let num=(num-x)^m
    echo "-$num"
  else
    let num=num\&m
    echo "$num"
  fi
done <file.txt
futurama ★★★★★
()
Последнее исправление: futurama (всего исправлений: 2)
Ответ на: комментарий от futurama

красиво это как-то так?

Вы не поняли, я не просил убирать $(( )), let для bash устаревший синтаксис, я к тому, что юзать $var внутри $(( )) надо только для специальных случаев, как раз парочка таких там есть: ((2#$Line)), но остальные достаточно num=$((num ^ m)) и так далее. Для [[ -ge ]] - поняли правильно, ведь это одно из отличий [[ ]] от [ ], для чего и делалось в том числе.

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

Весь этот синтаксический сахар не влияет на быстродействие баша.

Видите ли, внутри (( )) переменные резольвятся тривиально, а на код резольинга $var страшно смотреть, уж поверьте, я не только там смотрел, но и правил его в dash с принятием патчей в апстрим.

vodz ★★★★★
()