How to Execute Shell Built-in Commands Like ‘source’ in Puppet


10 views

When trying to automate shell configuration management with Puppet, you might hit a fundamental limitation: Puppet's exec type can't directly execute shell built-in commands. The source command is particularly problematic since it's not an executable binary but a Bash internal function.


# This WON'T work:
exec { "load_bashrc":
    command => "source /root/.bashrc",  # Will throw "command not found"
    path    => ["/bin", "/usr/bin"]     # Irrelevant for built-ins
}

Here are three reliable methods to achieve the same result:

Method 1: Invoke Through Bash


exec { "source_bashrc":
    command     => "/bin/bash -c 'source /root/.bashrc'",
    subscribe   => File["/root/.bashrc"],
    refreshonly => true,
    environment => ["HOME=/root"],
    user        => "root"
}

Method 2: Using Dot Notation


exec { "dot_bashrc":
    command     => "/bin/bash -c '. /root/.bashrc'",
    subscribe   => File["/root/.bashrc"],
    refreshonly => true
}

Method 3: Re-executing Shell

For login shells, you might need:


exec { "reload_bashrc":
    command     => "exec /bin/bash --login",
    subscribe   => File["/root/.bashrc"],
    refreshonly => true,
    user        => "root",
    environment => ["HOME=/root"]
}

1. Always include refreshonly => true when using subscribe to prevent unnecessary executions.

2. For system-wide changes, consider using /etc/profile.d/ instead of modifying individual .bashrc files.

3. Environment variables may need explicit declaration:


exec { "env_bashrc":
    environment => [
        "HOME=/root",
        "USER=root",
        "SHELL=/bin/bash"
    ],
    ...
}

For complex shell configurations, consider:


augeas { "modify_bashrc":
    context => "/files/root/.bashrc",
    changes => [
        "set PS1 '\"\$$\\e[1;32m\$$\\u@\\h:\\w\\$\$$\\e[0m\$$ \"'",
        "set PATH[. = '\$PATH'] '\$PATH:/custom/path'",
    ],
    notify  => Exec["reload_bashrc"]
}

When executing shell commands as root:

  1. Always specify the full path to binaries
  2. Consider using onlyif or unless to prevent redundant executions
  3. Audit commands with Puppet's noop mode first

When attempting to source ~/.bashrc through Puppet's exec resource, you'll encounter a fundamental limitation - Puppet executes commands directly via /bin/sh rather than through an interactive shell session. This means built-in shell commands like source, alias, or cd won't work as expected.

The error message Could not find command 'source' occurs because:

  • Shell built-ins don't exist as standalone executables
  • Puppet's exec doesn't load the user's shell environment
  • The source command requires bash-specific functionality

Here are three proven approaches to handle .bashrc sourcing in Puppet:

1. Using the Shell Provider Explicitly

exec { "source_bashrc":
  command     => "/bin/bash -c 'source /root/.bashrc'",
  subscribe   => File["/root/.bashrc"],
  refreshonly => true
}

2. Creating an Intermediate Script

file { "/usr/local/bin/refresh_bashrc":
  ensure  => present,
  mode    => 0755,
  content => "#!/bin/bash\nsource /root/.bashrc"
}

exec { "run_refresh_script":
  command     => "/usr/local/bin/refresh_bashrc",
  subscribe   => File["/root/.bashrc"],
  refreshonly => true
}

3. Environment Variable Alternative

exec { "update_environment":
  command     => "echo 'BASH_RELOADED=$(date +%s)' >> /root/.bashrc",
  subscribe   => File["/root/.bashrc"],
  refreshonly => true
}
  • Use refreshonly => true to prevent unnecessary executions
  • Consider whether immediate sourcing is truly needed (most changes don't require it)
  • For system-wide changes, /etc/profile.d/ scripts may be more appropriate

For complex scenarios, combine with Puppet's exported resources:

@@exec { "global_bashrc_refresh_${::fqdn}":
  command     => "/bin/bash -c 'source /root/.bashrc'",
  tag         => "bashrc_refresh",
  refreshonly => true
}

File["/root/.bashrc"] ~> Exec["global_bashrc_refresh_${::fqdn}"]

This creates a more robust relationship between the file modification and the sourcing operation.