BASH Tips&Tricks #0010: И на BASH'е будут join'иться массивы!
Когда я пишу на Perl, у меня буквально всё под рукой. Кое-что даже мешается и заставляет постоянно проверять кусочки кода в командной строке (perl -e или perl -E), дабы сделать полёт моей фантазии более комфортным и безопасным для здоровья разрабатываемой софтины.
Когда я пишу на BASH, мне, конечно же, многого не хватает: необъятные возможности работы с текстом в Perl'е накладывают свой отпечаток на отношение к другим языкам. Кое-что приходится терпеть, но многое вполне можно исправить, предоставив самому себе привычное окружение, пусть и несколько… задумчивое («бытует мнение», что в случае с BASH скоростью работы обычно можно пренебречь).
Так случилось и с функцией join, которой мне всегда так не хватало в BASH. Я, конечно же, говорю не о той join, которая «join lines of two files on a common field», а об одноимённой функции Perl, «склеивающей» элементы массива (если быть точным, то всё-таки «вектора») в строку.
Я долго терпел сие неудобство и всячески его игнорировал, но… в какой-то момент терпение моё лопнуло, и я решился на сотворение мира в атомарных масштаба. По итогам краткого ознакомления с бесплодными исканиями на StackOverflow и захватывающе познавательного обсуждения join-вопросов с умными людьми © на linux.org.ru, мною был исторгнут приведённый ниже код:
Бесспорно, он (код) как всегда великолепен и безупречен… пока мною же не будет доказано обратное :)
Квинтэссенция сути функции join2 (звучит как «Join To») заключается в том, что встроенная команда BASH printf понимает конструкцию ${arr[@]} не как уже интерполированную строку из элементов массива, объединённых абы чем (пробелами), но как именно набор элементов. Что эта белиберда значит? А то, что если какой-либо элемент массива уже содержит пробелы, а его объединят пробелом с элементом соседним, printf всё равно «увидит» каждый элемент в отдельности.
Пользоваться join2 можно двояко:
Cакральное знание об особенностях работы printf с массивами в BASHнагло сп скромно почёрпнуто из Yet Another BASH FAQ'а.
Когда я пишу на BASH, мне, конечно же, многого не хватает: необъятные возможности работы с текстом в Perl'е накладывают свой отпечаток на отношение к другим языкам. Кое-что приходится терпеть, но многое вполне можно исправить, предоставив самому себе привычное окружение, пусть и несколько… задумчивое («бытует мнение», что в случае с BASH скоростью работы обычно можно пренебречь).
Так случилось и с функцией join, которой мне всегда так не хватало в BASH. Я, конечно же, говорю не о той join, которая «join lines of two files on a common field», а об одноимённой функции Perl, «склеивающей» элементы массива (если быть точным, то всё-таки «вектора») в строку.
Я долго терпел сие неудобство и всячески его игнорировал, но… в какой-то момент терпение моё лопнуло, и я решился на сотворение мира в атомарных масштаба. По итогам краткого ознакомления с бесплодными исканиями на StackOverflow и захватывающе познавательного обсуждения join-вопросов с умными людьми © на linux.org.ru, мною был исторгнут приведённый ниже код:
join2 () {
(( $# == 2 || $# == 3 )) || return 1
local delim=$1
local arrName=$2
local join2var=$3
local v
for v in ${join2var:+join2var} arrName; do
[[ ${!v} =~ ^[_0-9A-Za-z]+$ ]] || return 2
done
[[ $join2var ]] || join2var='v'
source <(
cat <<SOURCE
printf -v $join2var "%s${delim}" "\${$arrName[@]}" || return \$?
[[ \$delim ]] && $join2var=\${$join2var:0:-${#delim}}
SOURCE
)
(( $# == 2 )) && echo "$v"
return 0
}
Бесспорно, он (код) как всегда великолепен и безупречен… пока мною же не будет доказано обратное :)
Квинтэссенция сути функции join2 (звучит как «Join To») заключается в том, что встроенная команда BASH printf понимает конструкцию ${arr[@]} не как уже интерполированную строку из элементов массива, объединённых абы чем (пробелами), но как именно набор элементов. Что эта белиберда значит? А то, что если какой-либо элемент массива уже содержит пробелы, а его объединят пробелом с элементом соседним, printf всё равно «увидит» каждый элемент в отдельности.
Пользоваться join2 можно двояко:
declare -a arr=('a b c' 'd e f' 'g h i') join2 '///' arr
— и в этом случае результат конкатенации элементов массива будет «выведен на экран», то есть отправлен на STDOUT, где его легко подобрать так:str=$(join2 '///' arr)
ИЛИ… (барабанная дробь, фанфары!)
declare -a arr=('a b c' 'd e f' 'g h i') join2 '///' arr str echo "$str"
— а в этом случае название функции окажется весьма неслучайным, поскольку результат join'а запишется сразу в переменную str.
Cакральное знание об особенностях работы printf с массивами в BASH