diff --git a/doc/lua-filters.md b/doc/lua-filters.md
index 851a061c5..f9c92528b 100644
--- a/doc/lua-filters.md
+++ b/doc/lua-filters.md
@@ -1075,6 +1075,20 @@ Lua functions for pandoc scripts.
 
         local fp = pandoc.mediabag.sha1("foobar")
 
+[`pipe (command, args, input)`]{#mediabag-sha1}
+
+:   Runs command with arguments, passing it some input,
+    and returns the exit code and the output.
+
+    Returns:
+
+    -   Exit code from command.
+    -   Output of command.
+
+    Usage:
+
+        local ec, output = pandoc.pipe("sed", {"-e","s/a/b/"}, "abc")
+
 
 # Submodule mediabag
 
diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs
index ecb1c530f..6a84a4350 100644
--- a/src/Text/Pandoc/Lua/PandocModule.hs
+++ b/src/Text/Pandoc/Lua/PandocModule.hs
@@ -48,6 +48,8 @@ import Text.Pandoc.Options (ReaderOptions(readerExtensions))
 import Text.Pandoc.Lua.StackInstances ()
 import Text.Pandoc.Readers (Reader (..), getReader)
 import Text.Pandoc.MIME (MimeType)
+import Text.Pandoc.Process (pipeProcess)
+import System.Exit (ExitCode(..))
 import Data.Digest.Pure.SHA (sha1, showDigest)
 
 import qualified Foreign.Lua as Lua
@@ -66,6 +68,9 @@ pushPandocModule datadir = do
   Lua.push "sha1"
   Lua.pushHaskellFunction sha1HashFn
   Lua.rawset (-3)
+  Lua.push "pipe"
+  Lua.pushHaskellFunction pipeFn
+  Lua.rawset (-3)
 
 -- | Get the string representation of the pandoc module
 pandocModuleScript :: Maybe FilePath -> IO String
@@ -109,6 +114,18 @@ sha1HashFn contents = do
   Lua.push $ showDigest (sha1 contents)
   return 1
 
+pipeFn :: String
+       -> [String]
+       -> BL.ByteString
+       -> Lua NumResults
+pipeFn command args input = do
+  (ec, output) <- liftIO $ pipeProcess Nothing command args input
+  Lua.push $ case ec of
+                  ExitSuccess   -> 0
+                  ExitFailure n -> n
+  Lua.push output
+  return 2
+
 insertMediaFn :: IORef MB.MediaBag
               -> FilePath
               -> OrNil MimeType