Как отключить подписку в DepositPhotos

Blog by admin
Это оказалось непростой задачей :) Пролазил все их менюшки, везде мне предлагалось только расширить пакет услуг. В FAQ тоже ничего не было, поэтому пришлось обращаться в техподдержку. Там подсказали вот что:
Для непрерывного сервиса на всех подписках включено автоматическое продление. Вы всегда можете самостоятельно отключить автоматическое продление в Вашем кабинете на сайте. Для того, чтобы отменить автопродление, пройдите в меню покупателя ru.depositphotos.com/home.html -> Настройки подписки (выпадающее окно) -> переключите с ON на OFF и далее следуйте подсказкам.

Пройдите по ссылке ru.depositphotos.com/image.html и выберите Настройки

Настройка fail2ban для защиты Elastix от подбора пароля для веб-интерфейса

Linux
Мануалов по настройке защиты asterisk много, а защиты самого elastix почему-то не нашел. Вопросы на форумах висят без ответов. Сделал сам, метод работает.
Создадим фильтр
vim /etc/fail2ban/filter.d/elastix-login.conf

[Definition]
#_daemon = asterisk
# Option:  failregex
# Notes.:  regex to match the password failures messages in the logfile. The
#          host must be matched by a group named "host". The tag "" can
#          be used for standard IP/hostname matching and is only an alias for
#          (?:::f{4,6}:)?(?P\S+)
# Values:  TEXT

#[Jul 14 20:47:04] LOGIN asdf: Authentication Failure to Web Interface login. Invalid user asdf from 192.168.0.100.
#[Nov 10 20:56:01] LOGIN admin: Authentication Failure to Web Interface login. Failed password for admin from 37.176.189.82.
#
failregex = LOGIN .*: Authentication Failure to Web Interface login. Invalid user .* from <HOST>.
            LOGIN .*: Authentication Failure to Web Interface login. Failed password for .* from <HOST>.

ignoreregex =

И добавим правило в jail.conf
[elastix]
enabled = true
filter = elastix-login
action = iptables-multiport[name=NoLoginFailures, port="http,https"]
logpath = /var/log/elastix/audit.log
bantime = 3600
maxretry = 3

Защита SSH с Fail2Ban в CentOS 7

Linux
Соединение по SSH к вашему серверу, доступному с интернета часто подвергается атакам. Fail2ban самое популярное средство для защиты от перебора паролей. Сервис сам анализирует неудачные попытки входа и создает правила в iptables, блокирующие доступ злоумышленникам.

Ниже проверенный мануал по установке Fail2ban на сервере CentOS 7.

Читать дальше

Asterisk GUI

Asterisk
Начал писать свой веб-интерфейс для asterisk, на php + js + html + ajax
Проект opensource. Пока еще на начальной стадии, но уже кое что умеет.
Из того, чего не видел у других — отображение очередей с статусами агентов и с кем они разговаривают (с привязкой к очереди), видно не отвеченные вызовы и нагрузку на очереди. Да, похожее есть в FOP2, но во-первых платно, во-вторых не совсем так.

Ссылка на github.com

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

Скриншот

Как добавить в очередь внешнего абонента

Asterisk
Нашел на форумах, возможно пригодится:

Вы можете добавить в очередь любой канал, в том числе внешний номер:

member => Local/12345@default

Где [default] наш контекст.

И так же можно использовать такую конструкцию:
member => SIP/12345@yourprovider

Как обновить PHP на CentOS 5 (6) до PHP 5.4 или PHP 5.5

Linux
Метод только, что проверил на Centos 5.11, обновил PHP 5.11 до PHP 5.5.
Смотрим какие пакеты PHP установлены:
# yum list installed | grep php

Если что-то есть, удаляем:
# yum remove php.x86_64 php-cli.x86_64 php-common.x86_64 php-gd.x86_64 php-ldap.x86_64 php-mbstring.x86_64 php-mcrypt.x86_64 php-mysql.x86_64 php-pdo.x86_64

Добавьте PHP 5.5 пакеты в yum если у вас CentOS 5.x
# rpm -Uvh http://mirror.webtatic.com/yum/el5/latest.rpm

Для CentOS 6.x:
# rpm -Uvh http://mirror.webtatic.com/yum/el6/latest.rpm

Теперь можно посмотреть какие пакеты доступны (5.4: php54w or 5.5: php55w):
# yum list available | grep php

Если нужна определенная версия:
# yum list available | grep php54

Ставим пакеты PHP 5.4 или 5.5, для примера я ставил php55:
# yum install php55w.x86_64 php55w-cli.x86_64 php55w-common.x86_64 php55w-gd.x86_64 php55w-ldap.x86_64 php55w-mbstring.x86_64 php55w-mcrypt.x86_64 php55w-mysql.x86_64 php55w-pdo.x86_64

Теперь можно посмотреть, какая версия установлена
# php -v
PHP 5.5.38 (cli) (built: Jul 21 2016 13:42:32)
Copyright © 1997-2015 The PHP Group
Zend Engine v2.5.0, Copyright © 1998-2015 Zend Technologies

Перезапустите сервис httpd:
# service httpd restart

Как собрать в архив все файлы rpm-пакета?

BASH
В продолжение аналогичного поста о dpkg предлагаю использовать следующий «наивный» вариант скрипта, собирающего те файлы, которые были скопированы в систему при установке rpm-пакета. Файлы, созданные автоматически INSTALL-скриптом пакета при этом «подобраны» не будут.

Для запуска скрипта нужно иметь возможность выполнять sudo под суперпользователем: это связано с тем, что немалая часть пакетов устанавливает файлы с эксклюзивным доступом к ним root'а: в таких случаях попытка создать tar-архив провалиться с треском.

Собственно код:

#!/bin/bash
[[ $1 == '-x' ]] && { shift; export TRACE=1; set -x; }
[[ $1 ]] || { echo 'You must specify package name or path to some file installed from the package' >&2; exit 1; }

if [[ ${1:0:1} == '/' ]]; then
        FILE=$1
        if ! PACKAGE=$(rpm -qf $(readlink -e $FILE)) 2>/dev/null || ! [[ $PACKAGE ]]; then
                echo 'Cant determine package name by file name' >&2
                exit 2
        fi
else
        PACKAGE=$1
        if ! PACKAGE=$(rpm -q $PACKAGE); then
                echo "This package seems to be not installed" >&2
                exit 3
        fi
fi

sudo bash <<EOSCRIPT
        tar -cjf "/tmp/${PACKAGE}.tbz2" -T <(
          while read f; do
                  [[ -d \$f ]] || echo "\$f"
          done < <(rpm -ql '${PACKAGE}')
        )
        chown "$(whoami)":"$(id -gn)" "/tmp/${PACKAGE}.tbz2"
        echo "/tmp/${PACKAGE}.tbz2 created" >&2 
EOSCRIPT


UPD: Поправил код скрипта, избавив его от 2-х вызовов sudo и пофиксив ошибки отсутствия присвавивания FILE=$1, а также ошибку сохранения короткого имени пакета, если было передано именно таковое.

Однострочник: получить дамп заголовков HTTP-трафика на первом ethernet-интерфейсе

Блог им. akkerman
Некоторые однострочники просто поражают своей лаконичностью, но могут быть не вполне функциональны.
Некоторые — написаны откровенно неэстетично и содержимое их вызывает блевотный рефлекс, но когда читать man'ы лениво — бегло сканируешь незамыленным глазом чей-то шедевр — и копируешь как есть, потому что… ну да, они «просто делают свою работу» (прямо как офисный планктон в Москве: просто делает что-то… как-то).
А есть однострочники, которые вроде и полезны весьма условно, но представляют интерес чисто академический: хочется разобрать их на винтики и понять, как же хиромантия сия устроена, что за магия заставляет вращаться чудесные шестерёнки Unix-way.
В качестве наглядного примера представляю на суд моих благодарных читателей (кстати, в последнее время я был безмерно счастлив отвечать на письма зело многочисленных поклонников моего скромного творчества) — вот этот многострочник, составленный из двух частей: одну легко найти на StackOverflow, а вторую приписал Ваш Покорный Слуга (здесь и далее — ВПС).

Вот:

sudo tcpdump -i $(ifconfig | sed -nr '/^[^[:space:]]/{ s%^([^[:space:]:]+).*$%\1%; h; b X}; /^\s*ether/{ g; p; q }; :X') -A -s 10240 'tcp port 3128 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' | egrep --line-buffered "^........(GET |HTTP\/|POST |HEAD )|^[A-Za-z0-9-]+: " | sed -r 's/^........(GET |HTTP\/|POST |HEAD )/\n\1/g'


Те истинные герои Совнгарда, которые первыми осознают и прочувствуют Суть работы механизмов радости в контексте представленного выше примера — да не сочтут они за труд отписаться в комментариях к посту, дабы подвиг их не канул в Лету.

Аминь!

P.S. Если вы низвергаетесь в грешный мир этих наших интернетов не через SQUID на дефолтном порту 3128, то замените циферки 3128 на что-то более другое. Спасибо!

4 проблемы устаревших систем телефонии, которые может решить IP-телефония

Asterisk
Недавнее исследование, проведенное Hanover Research показало, что, несмотря на рост электронной почты, видео и социальных медиа, 74% представителей малого и среднего бизнеса (SMB) говорит, что голосовая связь остается крайне или очень важным средством для бизнеса. Тем не менее, те предприятия, которые до сих пор имеют традиционные системы аналоговой телефонии (и которых до сих пор большинство), получили 4 проблемы, которые им придется разрешить в конце концов. И все они могут быть решены с помощью перехода на системы IP-телефонии (VoIP).

Устаревшее оборудование и сопровождение

Аппаратное обеспечение системы телефонии становится устаревшим, как только новая, передовая технология становится доступной. Устаревшие системы имеют типичные проблемы в виде поиска запчастей, которые бывают в наличии только на вторичном рынке, поэтому стоимость ремонта и поддержки повышаются. Стоит добавить техников и инженеров, которые были официально обучены на старых системах телефонии, которые уходят на пенсию или покидают компанию, а искать и обучать новых сложная задача. С повышенными рисками безопасности, возрастающими требованиями клиентов в коммуникациях с бизнесом, а также необходимостью повышения производительности, модель ограниченного в возможностях голосового общения быстро становится делом прошлого.

Ограниченная емкость и масштабируемость

Хорошо, когда ваша компания растет. И плохо, но многие предприятия перерастают свою инфраструктуру и им становится необходима система телефонии, которая может обрабатывать тот увеличивающийся объем вызовов, количества абонентов и данных, передаваемых по сети. Добавление новых территориально удаленных офисов в большинстве случаев является кошмаром в случае с традиционной системой, так как оборудование требуется в каждом новом здании. В случае с VoIP, так как все сотрудники в одной и той же IP-сети, не требуется никакого дополнительного оборудования, что экономит время и деньги. С традиционной АТС, добавление дополнительных линии для сотрудников может занять несколько недель. Если у вас система IP телефонии, процесс занимает несколько минут. Эта поддержка роста работает в обоих направлениях, например если у вас выдался тяжелый год и ваши потребности в росте уменьшаются, вы можете удалить часть линий и платить только за тот объем номеров и минут, которые вы используете.

Особенности и функциональные возможности

Системы IP-телефонии умеют маршрутизировать клиентов в соответствующий отдел без проигрывания меню. Зачем? Другие компании, которые уже реализовали системы VoIP имеют возможности для превосходного обслуживания клиентов, так что если ваш бизнес этого не делает, конкуренты будут воровать их. Сотрудники и клиенты нуждаются расширенных функциональных возможностях, чем 3-полосный вызов и идентификатор вызывающего абонента. Видео чат, обмен мгновенными сообщениями, состояние присутствия, голосовая почта по электронной почте, записи телефонных разговоров, аналитические функции, которые повышают производительность и качество обслуживания клиентов. Эти и другие функции предлагаются в решениях унифицированных коммуникаций (UC).

Ежемесячные сборы

Компании всегда ищут способы сократить расходы, но, что большинство не знают, что делать если это их телефонный счет. Когда новые клиенты звонят нам, один из вопросов, который мы задаем им, какой телефонный счет был в прошлом месяце. Девять из десяти из них не имеют ни малейшего представления, но они знают, что они, вероятно, должны знать, так как много денег, похоже, расходуется на связь каждый месяц. Переход на VoIP может сэкономить до 70% от ваших ежемесячных расходов на связь, немедленно. Если вы выбираете облачную систему IP-телефонии, вы сможете получить новую телефонную систему, которая заработает в течение часа (а не недель) и без каких-либо затрат. Кроме того, многие тарифные планы VoIP дают возможность ограниченного или многоканального плана оплаты, так что вы можете оплатить то количеству линий или минут, что используете в зависимости от того, что сейчас имеет больше смысла для вашей бизнес-модели.

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

Читать дальше

Автоматический обзвон абонентов, Asterisk автообзвон

Asterisk
Для этого нам понадобиться asterisk, ну и если ваш скрипт будет работать с базой тогда mysql

Конфигурация файлов asterisk


extensions.conf
Создадим контекст auto_dial, в котором опишем что и как мы проигрываем, и куда направляем исходящий вызов.

Код
[auto_dial]
exten => _X.,1,Dial(SIP/provider/${EXTEN:1})


Теперь проигрываем записанную запись
exten => s,1,Playback(Demo); - запись можно записать в формате mp3

Далее остается написать скрипт который будет создавать outgoing файлы.

Hам нужно будет помещать файлы в каталог /var/spool/asterisk/outgoing/, с таким содержанием
Channel: LOCAL/2222222@auto_dial - 2222222 -это телефон на который asterisk будет звонить и проигрывать demo.mp3.
CallerID: Ваш телефон - телефон который будет отображаться у него при определении
MaxRetries: 2
RetryTime: 10
WaitTime: 60
# MaxRetries: <number> количество попыток перезвонить
# RetryTime: <number> время между попытками перезвонить
# WaitTime: <number> сколько секунд ждать пока абонент ответит
Context: auto_dial
Extension: s
Priority: 2

Пример скрипта.
#!/usr/local/bin/perl
$file = "/var/spool/asterisk/outgoing/$ARGV[0]";
$phone = $ARGV[0];
open(FILE, "> $file");
print FILE "Channel: LOCAL/".$phone."\@auto_dial\n";
print FILE "CallerID: 5555555\n";
print FILE "MaxRetries: 2\n";
print FILE "RetryTime: 10\n";
print FILE "WaitTime: 60\n";
print FILE "Context: auto_dial\n";
print FILE "Extension: s\n";
print FILE "Priority: 2\n";
close(FILE);

Пример extensions.conf
[auto_dial]
exten => _X.,1,Dial(SIP/DenyaTelecom/${EXTEN:1})
exten => s,1,Playback(demo-congrats)

Это самый просто пример, который только показывает как это работает, вы вызываете этот скрипт вместе с телефоном на который нужно позвонить, пример:
./script.pl 8689390

То же самое на php, если нужно обзвонить пул номеров:
#!/usr/bin/php -q
<?php

$callto=111111; #первый номер списка
while ($callto<222222)
{
$callfile="/work/tmp/calls-".$callto.".call";
$cf =fopen($callfile,"w+"); 
fputs($cf,"Channel: SIP/".$callto."@trunkname\n");
fputs($cf,"Callerid: Your_callerid\n");
fputs($cf,"application: Playback\n");
fputs($cf,"Data:/work/zapisi\n");
fclose($cf);
copy($callfile,"/asteriskpath/var/spool/asterisk/outgoing/".$callto."call");
$callto=$callto+1;
if ($callto % 10 ==0) 
{
sleep(60); 
}
}
?>

sleep — для ограничие количества одновременных звонков

Как узнать модель материнской платы в Windows

Windows
Жмем Win-R, набираем cmd в открывшемся окне командной строки набираем
wmic baseboard get manufacturer, product

Результат например такой
C:\>wmic baseboard get manufacturer, product
Manufacturer  Product
Foxconn       H61MXE

Что мы еще можем собрать этим способом, например вот что:
Посмотреть объём памяти
wmic COMPUTERSYSTEM get TotalPhysicalMemory,caption

Посмотреть физически подключенные диски
wmic diskdrive get name,size,model

Посмотреть логические разделы на дисках
wmic volume WHERE DriveType=3 list brief

Просмотр информации о процессоре
wmic cpu get name, maxclockspeed

Просмотр информации о памяти
wmic MEMORYCHIP get banklabel, capacity, caption, devicelocator, partnumber

Скачать hyper terminal на Windows 7, 8, 8.1, 10

Blog by admin
Судя по запросам в поисковике тема еще актуальна, поэтому повторюсь. Скачать hyper terminal на Windows 7 (8, 8.1, 10) можно на этом сайте. Это та же программа, что была в Windows XP. Я Скопировал с XP 2 файла hypertrm.dll, hypertrm.exe и выложил на этом сайте. Кому нужны эти файлы, качайте.
HyperTetminal Windows 7 8 8.1 10

Скачать hypertrm.rar (hypertrm.dll, hypertrm.exe)

Ограничение входящих вызовов в Elastix (Asterisk)

Asterisk
Ограничить исходящие вызовы через SIP транк легко, для этого есть опция
call-limit=N

Но для входящих вызовов нужно использовать счетчик количества вызовов в диалплане. В случае чистого астериска это выглядит так:
exten => 559558,1,Set(GROUP()=group1)  ; Объединяем вызовы в группу
exten => 559558,n,Set(CALLS1=${GROUP_COUNT(group1)})  ; Считаем сколько вызовов в группе
exten => 559558,n,NoOp(${CALLS1})  ; Выводим в консоль счетчик (хотя строка выше и так должна вывести счетчик)
exten => 559558,n,ExecIf($[ ${CALLS1} > 4 ]?Hangup(34)) ; Если вызовов больше 4, сбрасываем. 34 это код отбоя  
exten => 559558,n,Dial(sip/101/${EXTEN},20)  ;  Отправляем вызов дальше

В случае с Elastix нужно добавить подобные строки в файл extensions_override_elastix.conf.
Читать дальше

Do You Know That? Как раскрывается "${array[@]}" и "${array[*]}"

BASH
Оказывается, в зависимости от режима интерполяции, внутри двойных кавычек массивы раскрываются принципиально по разному:

  • В случае с "${array[*]}" — массив интерполируется в один аргумент (одну строку), представляющий собой результат простой конкатенации всех элементов массива через пробел
  • В случае "${array[@]}" — каждый элемент массива становится отдельным аргументом так, словно каждый элемент взял себе внешние двойные кавычки, что уберегло его от дробления по пробельному символу (точнее, по IFS)

Тестовый пример:

Читать дальше

Вдогонку к Tips&Tricks'у #10h

BASH
Конечно же, для функции join2 должна быть и комплементарная split.

Учитывая то, что просто split — это «split a file into pieces», а также принимая во внимание тот факт, что split-функция всё-таки будет писать в переменную-массив, а не просто на STDOUT, я добавил к привычному имени циферку 2, дабы получилось у нас «SplitTo (the) arrayName».

Ну а теперь код:

split2 () {
        local arrName=$1
        [[ $arrName =~ ^[0-9_A-Za-z]+$ ]] || return 1
        local delim=$2
        shift 2
        local args
        while (( $# )); do
         args+=${args:+$delim}$1
         shift
        done  
        readarray -t $arrName < <(echo -n "${args//$delim/$'\x0a'}")
        return $?
}


Пример использования:

split2 dir '/' '/usr/share/doc/LaTeX' 'a/b' 'c/d/e f g/h'
declare -p dir
# OUTPUT:
# declare -a dir='([0]="" [1]="usr" [2]="share" [3]="doc" [4]="LaTeX" [5]="a" [6]="b" [7]="c" [8]="d" [9]="e f g" [10]="h")'


Enjoy! :)

BASH Tips&Tricks #0010: И на BASH'е будут join'иться массивы!

BASH
Когда я пишу на 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 () {
 (( $# == 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 можно двояко:
  1. 
    declare -a arr=('a b c' 'd e f' 'g h i')
    join2 '///' arr
    

    — и в этом случае результат конкатенации элементов массива будет «выведен на экран», то есть отправлен на STDOUT, где его легко подобрать так:
    str=$(join2  '///' arr)


    ИЛИ… (барабанная дробь, фанфары!)
  2. 
    declare -a arr=('a b c' 'd e f' 'g h i')
    join2 '///' arr str
    echo "$str"
    

    — а в этом случае название функции окажется весьма неслучайным, поскольку результат join'а запишется сразу в переменную str.

Cакральное знание об особенностях работы printf с массивами в BASH нагло сп скромно почёрпнуто из Yet Another BASH FAQ'а.

Пример преобразования форматов на BASH 4

BASH
Если кто-нибудь скажет Вам, что BASH — это какой-то недостойный внимания недоязык для написания циклов из командочек, киньте ему ссылку на данный пример.

Пример совсем несложный, я только что написал его для StackOverflow (где, к сожалению, люди всё ещё живут в криогенных камерах, не ведая о существовании BASH 4-й версии):

#!/bin/bash
inFile=$1
outFile=$2

join () {
 local del=$1
 shift
 IFS="$del"
 source <(
        cat <<SOURCE
 echo "\${$1[*]}"
SOURCE
 ) 
 unset IFS
}

declare -a CSV=('"Module Name","Module Group","Module Version"')
declare -a keysAccepted=('Name' 'Group' 'Version')

declare -i nMandatoryKeys=${#keysAccepted[@]}
declare -A KeyFilled
rxKeysAccepted='('$(join '|' keysAccepted)')'
while read line; do
        [[ $line =~ \<strong\>Module\ $rxKeysAccepted:\</strong\>[[:space:]]*([^<]+)\</p\> ]] || continue
        key=${BASH_REMATCH[1]}
        val=${BASH_REMATCH[2]}
        KeyFilled[$key]=$val
        if (( ${#KeyFilled[@]} == nMandatoryKeys )); then
                unset csvLine
                for k in ${keysAccepted[@]}; do
                        csvLine+=${csvLine:+,}${KeyFilled[$k]}
                done
                KeyFilled=()
                CSV+=($csvLine)
        fi
done <"$inFile"

(( ${#CSV[@]} > 1 )) || exit 1

join $'\x0a' CSV >"$outFile"


BASH-скрипт состоит из sed'ов, awk и grep'ов?
Давайте подсчитаем количество вхождений того, другого и третьего в данном примере!

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

Но, например, не нагляднее (Perl немного провоцирует писать супер-лаконичную абракадарбру). А является ли свойством BASH как языка программирования его действительно порой раздражающая медлительность — это ещё большой вопрос.

Do You Know That? Особенности mkdir -p

Блог им. akkerman
Утилита mkdir из GNU coreutils (во всех Linux) отличается следующими особенностями работы в режиме создания иерархии каталогов «на лету»:

  1. Если целевой каталог существует — mkdir с ключом -p не только не сообщит Вам об этом, но и не вернёт код ошибки
  2. Если добавить ключ -v (verbose) впридачу к -p — mkdir сообщит на STDOUT о каждом созданном ею каталоге. Именно по отсутствию какого-либо вывода в режиме -vp легко понять, что целевой каталог уже существовал на момент попытки его создания
  3. Всем создаваемым каталогам, кроме целевого, будет присвоена маска доступа, установленная umask (или действующая по умолчанию), а не та, которую Вы укажете в качестве значения для ключа -m утилиты mkdir/ Тем не менее, если Вы всё же укажете маску значением ключа -m, она будет использована при создании целевого каталога