Кусочки скриптов Bash. Обработка строк.

Несколько примеров обработки строк на Bash.
Была задача проверять содержимое шифрованных ZIP-архивов, описание создания которых дано в предыдущей заметке, доставать каким-то образом оттуда референс-код партнера, вставлять в заголовок письма и писать отдельно в лог.
Код партнера должен быть в названии документа, но, так как документы создаются вручную и собираются со всего мира, то никакой системы нет. Хорошо, что все партнеры используют собственную систему кодов, это позволяет вести поиск по шаблону.

В общем виде для кода фиксированной длинны L-M999999.99 это выглядит примерно так:
str1=`zipinfo -1 $filenamе | grep "L-M" | head -1`
if [ -z "$str1" ]
then
ref='NONREF'
else
idx=`echo $str1 | grep -b -o "L-M" | cut -d: -f1`
bgn=$(( $idx + 1 ))
end=$(( $idx + 12 ))
ref=`echo $str1 | cut -c $bgn-$end`
fi

[ -z "$str1" ] – проверка определена ли и не пустая ли переменная
grep -b -o "L-M" | cut -d: -f1 – определение после какой позиции начинается искомая подстрока

Вообще, grep очень мощная и гибкая утилита. Но не все параметры очевидны.
Например, wildcard обозначается точкой: echo $str1 | grep -b -o "XZ.-"
Очень полезный туториал по grep можно найти на Digital Ocean: “Using Grep & Regular Expressions to Search for Text Patterns in Linux”.

Более сложный вариант со “славянской спецификой”. Мало того, что начальная часть кода переменной длины (99999-DDMMYY-AAA / 99999А-DDMMYY-AAA), так она еще может содержать как латинскую, так и кириллическую букву “А”, какая раскладка была включена, так и вбили.

str1=`zipinfo -1 $filename | sed 's/\xd0\x90/A/g'|grep -E "[[:digit:]]{6}-[[:alnum:]]{3}" | head -1`
if [ -z "$str1" ]
then
ref='NONREF'
else
idx=`echo $str1 | grep -E -b -o "[[:digit:]]{6}-[[:alnum:]]{3}" | cut -d: -f1`
# if reference number contains letter 'A' it is one character longer
char=`echo $str1 | grep -E -b -o "[[:digit:]]{6}-[[:alnum:]]{3}" | cut -c $(( $idx - 1 ))`
if [ "$(echo $char | grep "^[ [:digit:] ]*$")" ]
then
bgn=$(( $idx - 5 ))
else
bgn=$(( $idx - 6 ))
fi
end=$(( $idx + 10 ))
ref=`echo $str1 | cut -c $bgn-$end`
fi

grep -E "[[:digit:]]{6}-[[:alnum:]]{3}" – поиск по шаблону 6 цифр “-” 3 цифро-буквенных символа
[ "$(echo $char | grep "^[ [:digit:] ]*$")" ] – проверка является ли символ цифрой
sed 's/\xd0\x90/A/g' – замена кириллической “А” на латинскую “A”, используются шестнадцатиричные коды.

Более продвинутый вариант, так как партнеры все делают вручную и никак не могут определиться в каком регистре будут символы и надо ли ставить в коде дефис. Код вида 99-YYlv, где первая часть варьируется по длине от 2 до 3 цифр:

str1=`zipinfo -1 $filename | tr [A-Z] [a-z]| sed 's/-//g' | grep -E "[[:digit:]]{4}lv" | head -1`
if [ -z "$str1" ]
then
ref='NONREF'
else
idx=`echo $str1 | tr [A-Z] [a-z]| sed 's/-//g' | grep -E -b -o "[[:digit:]]{4}lv" | cut -d: -f1`
char=`echo $str1 | tr [A-Z] [a-z]| sed 's/-//g' | grep -E "[[:digit:]]{4}lv" | cut -c $idx`
if [ "$(echo $char | grep "^[ [:digit:] ]*$")" ]
then
bgn=$idx
else
bgn=$(( $idx + 1 ))
fi
end=$(( $idx + 6 ))
ref=`echo $str1 | cut -c $bgn-$(( $end -4 ))`-`echo $str1 | cut -c $(( $end -3 ))-$end`
fi

tr [A-Z] [a-z] – самый простой способ поменять регистр строки.

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

Самый извращенный вариант обработки, первая часть кода варьируется от 3 до 6 цифр – простой счетчик без заполняющих нулей вида 999999-YYYY.

str1=`zipinfo -1 $filename | grep -E "[[:digit:]]{3}-[[:digit:]]{4}" | head -1`
if [ -z "$str1" ]
then
ref='NONREF'
else
idx=`echo $str1 | grep -E -b -o "[[:digit:]]{3}-[[:digit:]]{4}" | cut -d: -f1`
# partner code left part can be up to 6 digits long
char1=`echo $str1 | grep -E "[[:digit:]]{3}-[[:digit:]]{4}" | cut -c $idx`
char2=`echo $str1 | grep -E "[[:digit:]]{3}-[[:digit:]]{4}" | cut -c $(( $idx - 1 ))`
char3=`echo $str1 | grep -E "[[:digit:]]{3}-[[:digit:]]{4}" | cut -c $(( $idx - 2 ))`
if [ "$(echo $char1 | grep "^[ [:digit:] ]*$")" ]
then
if [ "$(echo $char2 | grep "^[ [:digit:] ]*$")" ]
then
if [ "$(echo $char3 | grep "^[ [:digit:] ]*$")" ]
then
bgn=$(( $idx - 2 ))
else
bgn=$(( $idx - 1 ))
fi
else
bgn=$idx
fi
else
bgn=$(( $idx + 1 ))
fi

end=$(( $idx + 8 ))
ref=`echo $str1 | cut -c $bgn-$end`
fi

Полный код высчитывается итерационно. Ничего более приличного в голову не приходит.

P.S.Краткий справочник по арифметическим операциям в bash: “Bash:Perform arithmetic operations”

Leave a comment

Your comment