Maksim Luzik
Maksim Luzik

Reputation: 6733

Running nested commands in Jenkins pipeline shell

I want to run nested shell command for example like this in Jenkins pipeline:

docker stop $(docker ps -aq)

Unfortunately when I format it into pipeline syntax:

sh('docker stop $(docker ps -aq)')

Jenkins does not seem to run them correctly, but outputs that:

"docker stop" requires at least 1 argument(s).

I tried to run the command under bash like told here: Run bash command on jenkins pipeline But end up with similar issue. Any ideas how to solve this?

Upvotes: 4

Views: 3930

Answers (5)

Akhil
Akhil

Reputation: 99

          stage('Some stage') {
                steps {
                    script{
                        def doc_containers = sh(returnStdout: true, script: 'docker container ps -aq').replaceAll("\n", " ") 
                        if (doc_containers) {
                            sh "docker rm -f ${doc_containers}"
                        }
                    }
                 }
              }

This worked for me on a centos linux agent

Note: docker rm -f removes all containers even if its running

Upvotes: 0

Muhammad Tariq
Muhammad Tariq

Reputation: 4664

Adding to the @Matt's answer,

You need a check for empty container. In case when there is no container available to stop, Jenkins build will fail and throw the following error message

"docker stop" requires at least 1 argument(s).

To handle this, you simply need a check for container availability. Here is the complete code

        stage('Clean docker containers'){
            steps{
                script{
                
                    def doc_containers = sh(returnStdout: true, script: 'docker container ps -aq').replaceAll("\n", " ") 
                    if (doc_containers) {
                        sh "docker stop ${doc_containers}"
                    }
                    
                }
            }
        }

Upvotes: 1

R. Adams
R. Adams

Reputation: 65

I've not used docker stop command this way but syntax is same for docker rm command. Block of pipeline code + OP's line for example:

...
    withEnv(["port=$port", "user=$user", "env=$env"]) {
        sh '''
            ssh -p $port $user@$env docker rm \$(docker ps -aq) || true; \
            ssh -p $port $user@$env docker rmi \$(docker images -aq) || true; \
            ssh -p $port $user@$env docker stop \$(docker ps -aq) || true
        '''
    }
...

Upvotes: 2

Matthew Schuchard
Matthew Schuchard

Reputation: 28774

This becomes easier for Jenkins Pipeline if you expand the shell command into two lines:

  • The first to capture the Docker containers that you want to stop.
  • The second to stop those Docker containers captured in the first command.

We use the first line to capture the output of the shell command into a variable:

containers = sh(returnStdout: true, script: 'sudo /usr/bin/docker ps -aq')

We then use the second command to operate on the captured output from the first command stored in a variable:

sh("sudo /usr/bin/docker stop $containers")

Note that the docker command is normally comfortable with the output of docker ps -aq for operating on with its other commands, but if it dislikes the output stored in the variable, you can reformat it like the following:

containers = sh(returnStdout: true, script: 'sudo /usr/bin/docker ps -aq').trim()

This would, for example, strip the leading whitespace and trailing newlines. The Docker CLI normally does not care about that, but some reformatting may prove necessary here.

Since removing the newlines here would result in a long combined container ID, we need to (as you noted) replace it with a whitespace to delimit the container IDs. That would make the formatting for the string stored in the containers variable:

containers = sh(returnStdout: true, script: 'sudo /usr/bin/docker ps -aq').replaceAll("\n", " ")

Upvotes: 5

user2611877
user2611877

Reputation: 1

Have you tried somethink like:

docker stop `docker ps -aq`

?

Upvotes: 0

Related Questions