summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.markdown34
-rw-r--r--lib/puppet/parser/functions/shell_escape.rb30
-rw-r--r--lib/puppet/parser/functions/shell_join.rb31
-rw-r--r--lib/puppet/parser/functions/shell_split.rb26
-rw-r--r--spec/functions/shell_escape_spec.rb22
-rw-r--r--spec/functions/shell_join_spec.rb23
-rw-r--r--spec/functions/shell_split_spec.rb24
7 files changed, 190 insertions, 0 deletions
diff --git a/README.markdown b/README.markdown
index 2f5faf3..9e62d1c 100644
--- a/README.markdown
+++ b/README.markdown
@@ -821,6 +821,40 @@ Strips spaces to the right of the string. *Type*: rvalue.
Takes an integer max value and a string seed value and returns a repeatable random integer smaller than max. Like `fqdn_rand`, but does not add node specific data to the seed. *Type*: rvalue.
+#### `shell_escape`
+
+Escapes a string so that it can be safely used in a Bourne shell command line. Note that the resulting string should be used unquoted and is not intended for use in double quotes nor in single quotes. This function behaves the same as ruby's `Shellwords.shellescape()` function, also see the [ruby documentation](http://ruby-doc.org/stdlib-2.3.0/libdoc/shellwords/rdoc/Shellwords.html#method-c-shellescape).
+
+*Example:*
+~~~
+shell_escape('foo b"ar') => 'foo\ b\"ar'
+~~~
+
+*Type*: rvalue.
+
+#### `shell_join`
+
+Builds a command line string from the given array of strings. Each array item is escaped for Bourne shell. All items are
+then joined together, with a single space in between. This function behaves the same as ruby's `Shellwords.shelljoin()` function, also see the [ruby documentation](http://ruby-doc.org/stdlib-2.3.0/libdoc/shellwords/rdoc/Shellwords.html#method-c-shelljoin).
+
+*Example:*
+~~~
+shell_join(['foo bar', 'ba"z']) => 'foo\ bar ba\"z'
+~~~
+
+*Type*: rvalue.
+
+#### `shell_split`
+
+Splits a string into an array of tokens in the same way the Bourne shell does. This function behaves the same as ruby's `Shellwords.shellsplit()` function, also see the [ruby documentation](http://ruby-doc.org/stdlib-2.3.0/libdoc/shellwords/rdoc/Shellwords.html#method-c-shellsplit).
+
+*Example:*
+~~~
+shell_split('foo\ bar ba\"z') => ['foo bar', 'ba"z']
+~~~
+
+*Type*: rvalue.
+
#### `shuffle`
Randomizes the order of a string or array elements. *Type*: rvalue.
diff --git a/lib/puppet/parser/functions/shell_escape.rb b/lib/puppet/parser/functions/shell_escape.rb
new file mode 100644
index 0000000..447fe35
--- /dev/null
+++ b/lib/puppet/parser/functions/shell_escape.rb
@@ -0,0 +1,30 @@
+#
+# shell_escape.rb
+#
+
+require 'shellwords'
+
+module Puppet::Parser::Functions
+ newfunction(:shell_escape, :type => :rvalue, :doc => <<-EOS
+Escapes a string so that it can be safely used in a Bourne shell command line.
+
+Note that the resulting string should be used unquoted and is not intended for use in double quotes nor in single
+quotes.
+
+This function behaves the same as ruby's Shellwords.shellescape() function.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "shell_escape(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ # explicit conversion to string is required for ruby 1.9
+ string = arguments[0].to_s
+
+ result = Shellwords.shellescape(string)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/shell_join.rb b/lib/puppet/parser/functions/shell_join.rb
new file mode 100644
index 0000000..05aeb95
--- /dev/null
+++ b/lib/puppet/parser/functions/shell_join.rb
@@ -0,0 +1,31 @@
+#
+# shell_join.rb
+#
+
+require 'shellwords'
+
+module Puppet::Parser::Functions
+ newfunction(:shell_join, :type => :rvalue, :doc => <<-EOS
+Builds a command line string from the given array of strings. Each array item is escaped for Bourne shell. All items are
+then joined together, with a single space in between.
+
+This function behaves the same as ruby's Shellwords.shelljoin() function
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "shell_join(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ array = arguments[0]
+
+ raise Puppet::ParseError, ("First argument is not an Array: #{array.inspect}") unless array.is_a?(Array)
+
+ # explicit conversion to string is required for ruby 1.9
+ array = array.map { |item| item.to_s }
+ result = Shellwords.shelljoin(array)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/shell_split.rb b/lib/puppet/parser/functions/shell_split.rb
new file mode 100644
index 0000000..0446448
--- /dev/null
+++ b/lib/puppet/parser/functions/shell_split.rb
@@ -0,0 +1,26 @@
+#
+# shell_split.rb
+#
+
+require 'shellwords'
+
+module Puppet::Parser::Functions
+ newfunction(:shell_split, :type => :rvalue, :doc => <<-EOS
+Splits a string into an array of tokens in the same way the Bourne shell does.
+
+This function behaves the same as ruby's Shellwords.shellsplit() function
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "shell_split(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ string = arguments[0].to_s
+
+ result = Shellwords.shellsplit(string)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/spec/functions/shell_escape_spec.rb b/spec/functions/shell_escape_spec.rb
new file mode 100644
index 0000000..3061dec
--- /dev/null
+++ b/spec/functions/shell_escape_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe 'shell_escape' do
+ it { is_expected.not_to eq(nil) }
+
+ describe 'signature validation' do
+ it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) }
+ it { is_expected.to run.with_params('foo', 'bar').and_raise_error(Puppet::ParseError, /wrong number of arguments/i) }
+ end
+
+ describe 'stringification' do
+ it { is_expected.to run.with_params(10).and_return('10') }
+ it { is_expected.to run.with_params(false).and_return('false') }
+ end
+
+ describe 'escaping' do
+ it { is_expected.to run.with_params('foo').and_return('foo') }
+ it { is_expected.to run.with_params('foo bar').and_return('foo\ bar') }
+ it { is_expected.to run.with_params('~`!@#$%^&*()_+-=[]\{}|;\':",./<>?')
+ .and_return('\~\`\!@\#\$\%\^\&\*\(\)_\+-\=\[\]\\\\\{\}\|\;\\\':\",./\<\>\?') }
+ end
+end
diff --git a/spec/functions/shell_join_spec.rb b/spec/functions/shell_join_spec.rb
new file mode 100644
index 0000000..6815f7c
--- /dev/null
+++ b/spec/functions/shell_join_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe 'shell_join' do
+ it { is_expected.not_to eq(nil) }
+
+ describe 'signature validation' do
+ it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) }
+ it { is_expected.to run.with_params(['foo'], ['bar']).and_raise_error(Puppet::ParseError, /wrong number of arguments/i) }
+ it { is_expected.to run.with_params('foo').and_raise_error(Puppet::ParseError, /is not an Array/i) }
+ end
+
+ describe 'shell argument joining' do
+ it { is_expected.to run.with_params(['foo']).and_return('foo') }
+ it { is_expected.to run.with_params(['foo', 'bar']).and_return('foo bar') }
+ it { is_expected.to run.with_params(['foo', 'bar baz']).and_return('foo bar\ baz') }
+ it { is_expected.to run.with_params(['~`!@#$', '%^&*()_+-=', '[]\{}|;\':"', ',./<>?'])
+ .and_return('\~\`\!@\#\$ \%\^\&\*\(\)_\+-\= \[\]\\\\\{\}\|\;\\\':\" ,./\<\>\?') }
+ end
+
+ describe 'stringification' do
+ it { is_expected.to run.with_params([10, false, 'foo']).and_return('10 false foo') }
+ end
+end
diff --git a/spec/functions/shell_split_spec.rb b/spec/functions/shell_split_spec.rb
new file mode 100644
index 0000000..beeb977
--- /dev/null
+++ b/spec/functions/shell_split_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe 'shell_split' do
+ it { is_expected.not_to eq(nil) }
+
+ describe 'signature validation' do
+ it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) }
+ it { is_expected.to run.with_params('foo', 'bar').and_raise_error(Puppet::ParseError, /wrong number of arguments/i) }
+ end
+
+ describe 'stringification' do
+ it { is_expected.to run.with_params(10).and_return(['10']) }
+ it { is_expected.to run.with_params(false).and_return(['false']) }
+ end
+
+ describe 'shell line spliting' do
+ it { is_expected.to run.with_params('foo').and_return(['foo']) }
+ it { is_expected.to run.with_params('foo bar').and_return(['foo', 'bar']) }
+ it { is_expected.to run.with_params('\~\`\!@\#\$\%\^\&\*\(\)_\+-\=\[\]\\\\\{\}\|\;\\\':\",./\<\>\?')
+ .and_return(['~`!@#$%^&*()_+-=[]\{}|;\':",./<>?']) }
+ it { is_expected.to run.with_params('\~\`\!@\#\$ \%\^\&\*\(\)_\+-\= \[\]\\\\\{\}\|\;\\\':\" ,./\<\>\?')
+ .and_return(['~`!@#$', '%^&*()_+-=', '[]\{}|;\':"', ',./<>?']) }
+ end
+end