{"_id":"puka","_rev":"146663","name":"puka","description":"A cross-platform library for safely passing strings through shells","dist-tags":{"latest":"1.0.1"},"maintainers":[{"name":"rhendric","email":"ryan.hendrickson@alum.mit.edu"}],"time":{"modified":"2021-06-03T11:30:07.000Z","created":"2017-10-02T21:11:05.419Z","1.0.1":"2020-05-16T05:30:08.214Z","1.0.0":"2017-10-02T21:11:05.419Z"},"users":{"ruyadorno":true},"author":{"name":"Ryan Hendrickson","email":"ryan.hendrickson@alum.mit.edu"},"repository":{"type":"git","url":"git+https://gitlab.com/rhendric/puka.git"},"versions":{"1.0.1":{"name":"puka","version":"1.0.1","description":"A cross-platform library for safely passing strings through shells","keywords":["args","arguments","cmd","command","command-line","cross-platform","escape","escaping","exec","linux","mac","macos","osx","quote","quoting","sh","shell","spawn","unix","win","win32","windows"],"homepage":"https://gitlab.com/rhendric/puka","bugs":{"url":"https://gitlab.com/rhendric/puka/issues"},"license":"MIT","author":{"name":"Ryan Hendrickson","email":"ryan.hendrickson@alum.mit.edu"},"repository":{"type":"git","url":"git+https://gitlab.com/rhendric/puka.git"},"dependencies":{},"engines":{"node":">=4"},"_id":"puka@1.0.1","_nodeVersion":"14.2.0","_npmVersion":"6.14.4","dist":{"shasum":"a2df782b7eb4cf9564e4c93a5da422de0dfacc02","size":15380,"noattachment":false,"key":"/puka/-/puka-1.0.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/puka/download/puka-1.0.1.tgz"},"maintainers":[{"name":"rhendric","email":"ryan.hendrickson@alum.mit.edu"}],"_npmUser":{"name":"rhendric","email":"ryan.hendrickson@alum.mit.edu"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/puka_1.0.1_1589607008109_0.8784867701969696"},"_hasShrinkwrap":false,"publish_time":1589607008214,"_cnpm_publish_time":1589607008214},"1.0.0":{"name":"puka","version":"1.0.0","description":"A cross-platform library for safely passing strings through shells","keywords":["args","arguments","cmd","command","command-line","cross-platform","escape","escaping","exec","linux","mac","macos","osx","quote","quoting","sh","shell","spawn","unix","win","win32","windows"],"homepage":"https://gitlab.com/rhendric/puka","bugs":{"url":"https://gitlab.com/rhendric/puka/issues"},"license":"MIT","author":{"name":"Ryan Hendrickson","email":"ryan.hendrickson@alum.mit.edu"},"repository":{"type":"git","url":"git+https://gitlab.com/rhendric/puka.git"},"dependencies":{},"engines":{"node":">=4"},"_id":"puka@1.0.0","_npmVersion":"5.4.2","_nodeVersion":"8.6.0","_npmUser":{"name":"rhendric","email":"ryan.hendrickson@alum.mit.edu"},"dist":{"shasum":"1dd92f9f81f6c53390a17529b7aebaa96604ad97","size":14109,"noattachment":false,"key":"/puka/-/puka-1.0.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/puka/download/puka-1.0.0.tgz"},"maintainers":[{"name":"rhendric","email":"ryan.hendrickson@alum.mit.edu"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/puka-1.0.0.tgz_1506978664326_0.009653010871261358"},"directories":{},"publish_time":1506978665419,"_hasShrinkwrap":false,"_cnpm_publish_time":1506978665419}},"readme":"# Puka\n\n[![GitLab CI pipeline status](https://gitlab.com/rhendric/puka/badges/master/pipeline.svg)](https://gitlab.com/rhendric/puka/commits/master) [![AppVeyor build status](https://img.shields.io/appveyor/ci/rhendric/puka.svg?label=windows%20tests)](https://ci.appveyor.com/project/rhendric/puka) [![Codecov status](https://img.shields.io/codecov/c/gl/rhendric/puka.svg)](https://codecov.io/gl/rhendric/puka)\n\nPuka is a cross-platform library for safely passing strings through shells.\n\n#### Contents\n\n-   [Introduction](#introduction)\n    -   [Why would I use Puka?](#why-would-i-use-puka)\n    -   [How do I use Puka?](#how-do-i-use-puka)\n    -   [What's the catch?](#whats-the-catch)\n-   [API Documentation](#api-documentation)\n    -   [Basic API](#basic-api)\n        -   [sh](#sh)\n        -   [unquoted](#unquoted)\n    -   [Advanced API](#advanced-api)\n        -   [quoteForShell](#quoteforshell)\n        -   [quoteForCmd](#quoteforcmd)\n        -   [quoteForSh](#quoteforsh)\n        -   [ShellString](#shellstring)\n    -   [Secret API](#secret-api)\n-   [The sh DSL](#the-sh-dsl)\n    -   [Syntax](#syntax)\n    -   [Semantics](#semantics)\n        -   [Types of placeholders](#types-of-placeholders)\n\n## Introduction\n\n### Why would I use Puka?\n\nWhen launching a child process from Node, you have a choice between launching\ndirectly from the operating system (as with [child_process.spawn](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options),\nif you don't use the `{ shell: true }` option), and running the command through\na shell (as with [child_process.exec](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)).\nUsing a shell gives you more power, such as the ability to chain multiple\ncommands together or use redirection, but you have to construct your command as\na single string instead of using an array of arguments. And doing that can be\nbuggy (if not dangerous) if you don't take care to quote any arguments\ncorrectly for the shell you're targeting, _and_ the quoting has to be done\ndifferently on Windows and non-Windows shells.\n\nPuka solves that problem by giving you a simple and platform-agnostic way to\nbuild shell commands with arguments that pass through your shell unaltered and\nwith no unsafe side effects, **whether you are running on Windows or a\nUnix-based OS**.\n\n### How do I use Puka?\n\nPuka gives you an `sh` function intended for tagging\n[template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals),\nwhich quotes (if necessary) any values interpolated into the template. A simple\nexample:\n\n```javascript\nconst { sh } = require('puka');\nconst { execSync } = require('child_process');\n\nconst arg = 'file with spaces.txt';\nexecSync(sh`some-command ${arg}`);\n```\n\nBut Puka supports more than this! See [the `sh` DSL documentation](#the-sh-dsl)\nfor a detailed description of all the features currently supported.\n\n### What's the catch?\n\nHere are the ones I know about:\n\nPuka does _not_ ensure that the actual commands you're running are\ncross-platform. If you're running npm programs, you generally won't have a\nproblem with that, but if you want to run ``sh`cat file` `` on Windows, you'll\nneed to depend on something like\n[cash-cat](https://www.npmjs.com/package/cash-cat).\n\nI searched for days for a way to quote or escape line breaks in arguments to\n`cmd.exe`, but couldn't find one (regular `^`-prepending and quotation marks\ndon't seem to cut it). If you know of a way that works, please [open an\nissue](https://gitlab.com/rhendric/puka/issues/new) to tell me about it! Until\nthen, any line break characters (`\\r` or `\\n`) in values being interpolated by\n`sh` will cause an error to be thrown on Windows only.\n\nAlso on Windows, you may notice quoting mistakes if you run commands that\ninvolve invoking a native executable (not a batch file ending in `.cmd` or\n`.bat`). Unfortunately, batch files require some extra escaping on Windows, and\nPuka assumes all programs are batch files because npm creates batch file shims\nfor programs it installs (and, if you care about cross-platform, you'll be\nusing npm programs in your commands). If this causes problems for you, please\n[open an issue](https://gitlab.com/rhendric/puka/issues/new); if your situation\nis specific enough, there may be workarounds or improvements to Puka to be\nfound.\n\n## API Documentation\n\n### Basic API\n\n\n\n\n#### sh\n\nA string template tag for safely constructing cross-platform shell commands.\n\nAn `sh` template is not actually treated as a literal string to be\ninterpolated; instead, it is a tiny DSL designed to make working with shell\nstrings safe, simple, and straightforward. To get started quickly, see the\nexamples below. [More detailed documentation][1] is available\nfurther down.\n\n##### Examples\n\n```javascript\nconst title = '\"this\" & \"that\"';\nsh`script --title=${title}`; // => \"script '--title=\\\"this\\\" & \\\"that\\\"'\"\n// Note: these examples show results for non-Windows platforms.\n// On Windows, the above would instead be\n// 'script ^^^\"--title=\\\\^^^\"this\\\\^^^\" ^^^& \\\\^^^\"that\\\\^^^\"^^^\"'.\n\nconst names = ['file1', 'file 2'];\nsh`rimraf ${names}.txt`; // => \"rimraf file1.txt 'file 2.txt'\"\n\nconst cmd1 = ['cat', 'file 1.txt', 'file 2.txt'];\nconst cmd2 = ['use-input', '-abc'];\nsh`${cmd1}|${cmd2}`; // => \"cat 'file 1.txt' 'file 2.txt'|use-input -abc\"\n```\n\nReturns **[String][2]** a string formatted for the platform Node is currently\nrunning on.\n\n#### unquoted\n\nThis function permits raw strings to be interpolated into a `sh` template.\n\n**IMPORTANT**: If you're using Puka due to security concerns, make sure you\ndon't pass any untrusted content to `unquoted`. This may be obvious, but\nstray punctuation in an `unquoted` section can compromise the safety of the\nentire shell command.\n\n##### Parameters\n\n-   `value`  any value (it will be treated as a string)\n\n##### Examples\n\n```javascript\nconst both = true;\nsh`foo ${unquoted(both ? '&&' : '||')} bar`; // => 'foo && bar'\n```\n\n### Advanced API\n\nIf these functions make life easier for you, go ahead and use them; they\nare just as well supported as the above. But if you aren't certain you\nneed them, you probably don't.\n\n\n#### quoteForShell\n\nQuotes a string for injecting into a shell command.\n\nThis function is exposed for some hypothetical case when the `sh` DSL simply\nwon't do; `sh` is expected to be the more convenient option almost always.\nCompare:\n\n```javascript\nconsole.log('cmd' + args.map(a => ' ' + quoteForShell(a)).join(''));\nconsole.log(sh`cmd ${args}`); // same as above\n\nconsole.log('cmd' + args.map(a => ' ' + quoteForShell(a, true)).join(''));\nconsole.log(sh`cmd \"${args}\"`); // same as above\n```\n\nAdditionally, on Windows, `sh` checks the entire command string for pipes,\nwhich subtly change how arguments need to be quoted. If your commands may\ninvolve pipes, you are strongly encouraged to use `sh` and not try to roll\nyour own with `quoteForShell`.\n\n##### Parameters\n\n-   `text` **[String][2]** to be quoted\n-   `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string\n    is already safe. Defaults to `false`.\n-   `platform` **[String][2]?** a value that `process.platform` might take:\n    `'win32'`, `'linux'`, etc.; determines how the string is to be formatted.\n    When omitted, effectively the same as `process.platform`.\n\nReturns **[String][2]** a string that is safe for the current (or specified)\nplatform.\n\n#### quoteForCmd\n\nA Windows-specific version of [quoteForShell][4].\n\n##### Parameters\n\n-   `text` **[String][2]** to be quoted\n-   `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string\n    is already safe. Defaults to `false`.\n\n#### quoteForSh\n\nA Unix-specific version of [quoteForShell][4].\n\n##### Parameters\n\n-   `text` **[String][2]** to be quoted\n-   `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string\n    is already safe. Defaults to `false`.\n\n#### ShellString\n\nA ShellString represents a shell command after it has been interpolated, but\nbefore it has been formatted for a particular platform. ShellStrings are\nuseful if you want to prepare a command for a different platform than the\ncurrent one, for instance.\n\nTo create a ShellString, use `ShellString.sh` the same way you would use\ntop-level `sh`.\n\n##### toString\n\nA method to format a ShellString into a regular String formatted for a\nparticular platform.\n\n###### Parameters\n\n-   `platform` **[String][2]?** a value that `process.platform` might take:\n    `'win32'`, `'linux'`, etc.; determines how the string is to be formatted.\n    When omitted, effectively the same as `process.platform`.\n\nReturns **[String][2]** \n\n##### sh\n\n`ShellString.sh` is a template tag just like `sh`; the only difference is\nthat this function returns a ShellString which has not yet been formatted\ninto a String.\n\nReturns **[ShellString][5]** \n\n### Secret API\n\nSome internals of string formatting have been exposed for the ambitious and\nbrave souls who want to try to extend Puka to handle more shells or custom\ninterpolated values. This ‘secret’ API is partially documented in the code\nbut not here, and the semantic versioning guarantees on this API are bumped\ndown by one level: in other words, minor version releases of Puka can change\nthe secret API in backward-incompatible ways, and patch releases can add or\ndeprecate functionality.\n\nIf it's not even documented in the code, use at your own risk—no semver\nguarantees apply.\n\n\n[1]: #the-sh-dsl\n\n[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String\n\n[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean\n\n[4]: #quoteforshell\n\n[5]: #shellstring\n\n## The sh DSL\n\n### Syntax\n\nAn `sh` template comprises words, separated by whitespace. Words can contain:\n\n-   text, which is composed of any characters that are not whitespace, single or\n    double quotes, or any of the special characters\n    ``# $ & ( ) ; < > \\ ` |``;\n-   quotations, which are matching single or double quotes surrounding any\n    characters other than the delimiting quote; and\n-   placeholders, using the standard JavaScript template syntax (`${}`).\n    (Placeholders may also appear inside quotations.)\n\nThe special characters ``# $ & ( ) ; < > \\ ` |``, if unquoted, form their own\nwords.\n\nRedirect operators (`<`, `>`, `>>`, `2>`, etc.) receive their own special\nhandling, as do semicolons. Other than these two exceptions, no attempt is made\nto understand any more sophisticated features of shell syntax.\n\nStandard JavaScript escape sequences, such as `\\t`, are honored in the template\nliteral, and are treated equivalently to the characters they represent. There\nis no further mechanism for escaping within the `sh` DSL itself; in particular,\nif you want to put quotes inside quotes, you have to use interpolation, like\nthis:\n\n```javascript\nsh`echo \"${'single = \\', double = \"'}\"` // => \"echo 'single = '\\\\'', double = \\\"'\"\n```\n\n### Semantics\n\nWords that do not contain placeholders are emitted mostly verbatim to the\noutput string. Quotations are formatted in the expected style for the target\nplatform (single quotes for Unix, double quotes for Windows) regardless of the\nquotes used in the template literal—as with JavaScript, single and double quotes\nare interchangeable, except for the requirement to pair like with like. Unquoted\nsemicolons are translated to ampersands on Windows; all other special characters\n(as enumerated above), when unquoted, are passed as-is to the output for the\nshell to interpret.\n\nPuka may still quote words not containing the above special characters, if they\ncontain characters that need quoting on the target platform. For example, on\nWindows, the character `%` is used for variable interpolation in `cmd.exe`, and\nPuka quotes it on on that platform even if it appears unquoted in the template\nliteral. Consequently, there is no need to be paranoid about quoting anything\nthat doesn't look alphanumeric inside a `sh` template literal, for fear of being\nburned on a different operating system; anything that matches the definition of\n‘text’ above will never need manual quoting.\n\n#### Types of placeholders\n\n##### Strings\n\nIf a word contains a string placeholder, then the value of the placeholder is\ninterpolated into the word and the entire word, if necessary, is quoted. If\nthe placeholder occurs within quotes, no further quoting is performed:\n\n```javascript\nsh`script --file=\"${'herp derp'}.txt\"`; // => \"script --file='herp derp.txt'\"\n```\n\nThis behavior can be exploited to force consistent quoting, if desired; but\nboth of the examples below are safe on all platforms:\n\n```javascript\nconst words = ['oneword', 'two words'];\nsh`minimal ${words[0]}`; // => \"minimal oneword\"\nsh`minimal ${words[1]}`; // => \"minimal 'two words'\"\nsh`consistent '${words[0]}'`; // => \"consistent 'oneword'\"\nsh`consistent '${words[1]}'`; // => \"consistent 'two words'\"\n```\n\n##### Arrays and iterables\n\nIf a word contains a placeholder for an array (or other iterable object), then\nthe entire word is repeated once for each value in the array, separated by\nspaces. If the array is empty, then the word is not emitted at all, and neither\nis any leading whitespace.\n\n```javascript\nconst files = ['foo', 'bar'];\nsh`script ${files}`; // => \"script foo bar\"\nsh`script --file=${files}`; // => \"script --file=foo --file=bar\"\nsh`script --file=${[]}`; // => \"script\"\n```\n\nNote that, since special characters are their own words, the pipe operator here\nis not repeated:\n\n```javascript\nconst cmd = ['script', 'foo', 'bar'];\nsh`${cmd}|another-script`; // => \"script foo bar|another-script\"\n```\n\nMultiple arrays in the same word generate a Cartesian product:\n\n```javascript\nconst names = ['foo', 'bar'], exts = ['log', 'txt'];\n// Same word\nsh`... ${names}.${exts}`; // => \"... foo.log foo.txt bar.log bar.txt\"\nsh`... \"${names} ${exts}\"`; // => \"... 'foo log' 'foo txt' 'bar log' 'bar txt'\"\n\n// Not the same word (extra space just for emphasis):\nsh`... ${names}   ${exts}`; // => \"... foo bar   log txt\"\nsh`... ${names};${exts}`; // => \"... foo bar;log txt\"\n```\n\nFinally, if a placeholder appears in the object of a redirect operator, the\nentire redirect is repeated as necessary:\n\n```javascript\nsh`script > ${['foo', 'bar']}.txt`; // => \"script > foo.txt > bar.txt\"\nsh`script > ${[]}.txt`; // => \"script\"\n```\n\n##### unquoted\n\nThe `unquoted` function returns a value that will skip being quoted when used\nin a placeholder, alone or in an array.\n\n```javascript\nconst cmd = 'script < input.txt';\nconst fields = ['foo', 'bar'];\nsh`${unquoted(cmd)} | json ${fields}`; // => \"script < input.txt | json foo bar\"\n```\n\n##### ShellString\n\nIf `ShellString.sh` is used to construct an unformatted ShellString, that value\ncan be used in a placeholder to insert the contents of the ShellString into the\nouter template literal. This is safer than using `unquoted` as in the previous\nexample, but `unquoted` can be used when all you have is a string from another\n(trusted!) source.\n\n```javascript\nconst url = 'http://example.com/data.json?x=1&y=2';\nconst curl = ShellString.sh`curl -L ${url}`;\nconst fields = ['foo', 'bar'];\nsh`${curl} | json ${fields}`; // => \"curl -L 'http://example.com/data.json?x=1&y=2' | json foo bar\"\n```\n\n##### Anything else\n\n... is treated like a string—namely, a value `x` is equivalent to `'' + x`, if\nnot in one of the above categories.\n","_attachments":{},"homepage":"https://gitlab.com/rhendric/puka","bugs":{"url":"https://gitlab.com/rhendric/puka/issues"},"license":"MIT"}