summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoris <joris@jorisvanderwel.com>2016-04-28 19:44:30 +0200
committerTP Honey <tphoney@users.noreply.github.com>2016-04-28 18:44:30 +0100
commit9e1f74f3fcac4aeaccade3ea92e6cafbaf71a64f (patch)
tree951a0596bb3dec824c1343d8837ec7ca9d9b10bc
parent24797bb5ad73eb4baee0567282ef2670ed141a5c (diff)
Expose the functions of ruby's built-in Shellwords module (#580)
* Add shell_escape function, shell_join function & shell_split function
-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