Понякога, работейки с променливи, особено съдържащи специални символи (като $), трябва да подхождаме по-внимателно.

ЗАБЕЛЕЖКА: Ако искате да тествате описаното по-долу, ще ви трябва машина с инсталирани Docker и Jenkins.

Нека да вземем за пример този код на процес в Jenkins:

pipeline {
    agent any
    
    environment 
    {
        PASS='Pa$$word'
    }

    stages {
        stage('Clean') {
            steps {
                sh 'docker container rm --force $(docker container ls -aq) || true'
            }
        }
        stage('Escape Test #1 - NOK') {
            steps {
                sh 'docker container run -d --name db1 -e MYSQL_ROOT_PASSWORD=Pa$$word mariadb'
            }
        }
        stage('Escape Test #2 - NOK') {
            steps {
                sh "docker container run -d --name db2 -e MYSQL_ROOT_PASSWORD=Pa\$\$word mariadb"
            }
        }
        stage('Escape Test #3 - OK') {
            steps {
                sh "docker container run -d --name db3 -e MYSQL_ROOT_PASSWORD='Pa\$\$word' mariadb"
            }
        }
        stage('Escape Test #4 - OK') {
            steps {
                sh 'docker container run -d --name db4 -e MYSQL_ROOT_PASSWORD=\'Pa\$\$word\' mariadb'
            }
        }
        stage('Escape Test #5 - OK') {
            steps {
                sh '''docker container run -d --name db5 -e MYSQL_ROOT_PASSWORD='Pa$$word' mariadb'''
            }
        }
        stage('Escape Test #6 - OK') {
            steps {
                sh 'docker container run -d --name db6 -e MYSQL_ROOT_PASSWORD=$PASS mariadb'
            }
        }
    }
}

ЗАБЕЛЕЖКА: MYSQL_ROOT_PASSWORD в горния пример може да бъде заменено с MARIADB_ROOT_PASSWORD.

На всяка стъпка се стартира по един контейнер с MariaDB. Разликата е в това как се подава паролата за root потребителя. При всички положения се очаква, че се подава парола със съдържание Pa$$word.

При изпълнение на процеса, той ще приключи успешно при това без явна индикация за потенциален проблем, поне не на пръв поглед. Да ама не.

Ако проверим в терминална сесия дали успяваме да комуникираме с базата, използвайки паролата, която сме задали, ще установим, че нещата не са съвсем както очакваме.

Подход 1 - неработещ:

$ docker container exec db1 mariadb -u root -p'Pa$$word' -e 'SHOW DATABASES;'
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
$

Подход 2 - неработещ:

$ docker container exec db2 mariadb -u root -p'Pa$$word' -e 'SHOW DATABASES;'
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
$

Подход 3 - работещ:

$ docker container exec db3 mariadb -u root -p'Pa$$word' -e 'SHOW DATABASES;'
Database
information_schema
mysql
performance_schema
sys
$

Подходи 4, 5 и 6 са също работещи и резултатът е същия като при подход 3.

Разбира се, този резултат не е съвсем необясним. За да разберем причината, е достатъчно да погледнем журнала от изпълнението на процеса.

Ето извадка за подход 1:

...
[Pipeline] { (Escape Test #1 - NOK)
[Pipeline] sh
+ docker container run -d --name db1 -e MYSQL_ROOT_PASSWORD=Pa6768word mariadb
...

И за подход 2:

...
[Pipeline] { (Escape Test #2 - NOK)
[Pipeline] sh
+ docker container run -d --name db2 -e MYSQL_ROOT_PASSWORD=Pa6948word mariadb
...

Можем да видим, че настъпва заместване на двата последователни символа $$ със стойност, отговаряща на номера на текущия процес. Т.е. вместо очакваното Pa$$word, ние получаваме нещо различно - в единия случай Pa6768word, а в другия - Pa6948word. Разбира се, при следващо изпълнение стойностите ще са други.

Това е. Просто трябва да бъдем внимателни за това как точно ограждаме командите, които ще изпълняваме и как обработваме специалните символи в тях. :)