shellsplit

function shellsplit( line ) --> args

Description

Splits a string into an array of tokens in the same way the UNIX Bourne shell does.

Parameters

line

Input line with the arguments.

Return Values

args

Array table with the tokens.

Code

--ZFUNC-shellsplit-v1
local function shellsplit( line ) --> args
   --ZFUNC-trim-v1
   local function trim( str )
      local n = str:find( "%S" )
      return n and str:match( ".*%S" ) or ""
   end
   --ZFUNC-utf8codes-v1
   local function utf8codes( str )
      return str:gmatch( "[%z\1-\127\194-\244][\128-\191]*" )
   end

   local function isspace( str )
      return str == " " or str == "\t" or str == "\r" or str == "\n"
   end

   line = trim( line )

   local args = {}
   local buff = {}
   local escaped, doubleQ, singleQ, backQ = false, false, false, false

   for r in utf8codes( line ) do
      if escaped then-----------------------------------------------------------
         table.insert( buff, r )
         escaped = false

      elseif r == '\\' then-----------------------------------------------------
         if singleQ then
            table.insert( buff, r )
         else
            escaped = true
         end

      elseif isspace( r ) then--------------------------------------------------
         if singleQ or doubleQ or backQ then
            table.insert( buff, r )
         else
            table.insert( args, table.concat( buff ) )
            buff = {}
         end

      elseif r == "`" then------------------------------------------------------
         if singleQ or doubleQ then
            table.insert( buff, r )
         else
            backQ = not backQ
         end

      elseif r == '"' then------------------------------------------------------
         if singleQ or backQ then
            table.insert( buff, r )
         else
            doubleQ = not doubleQ
         end

      elseif r == "'" then------------------------------------------------------
         if doubleQ or backQ then
            table.insert( buff, r )
         else
            singleQ = not singleQ
         end

      else----------------------------------------------------------------------
         table.insert( buff, r )

      end
   end

   if #buff > 0 then table.insert( args, table.concat( buff ) ) end

   return args
end

return shellsplit

Examples

local t = require( "taptest" )
local same = require( "same" )
local shellsplit = require( "shellsplit" )

shargs = shellsplit( 'here are "two words"' )
t( same( shargs, { "here", "are", "two words" } ), true )

shargs = shellsplit( 'here "two words" are' )
t( same( shargs, { "here", "two words", "are" } ), true )

shargs = shellsplit( "here are 'two words'" )
t( same( shargs, { "here", "are", "two words" } ), true )

shargs = shellsplit( "-dfs -ar='one two three'" )
t( same( shargs, { "-dfs", "-ar=one two three" } ), true )

t()

See also