{"id":22366731,"url":"https://github.com/vaeth/quoter","last_synced_at":"2025-08-25T20:08:18.602Z","repository":{"id":12476936,"uuid":"71935794","full_name":"vaeth/quoter","owner":"vaeth","description":"Quote arguments or standard input for usage in POSIX shell by eval","archived":false,"fork":false,"pushed_at":"2022-03-21T23:01:21.000Z","size":31,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-30T18:39:20.168Z","etag":null,"topics":["pipe","posix","quotes","quoting-args","shell"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vaeth.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-25T20:23:25.000Z","updated_at":"2024-11-01T18:30:45.000Z","dependencies_parsed_at":"2022-08-07T07:00:11.205Z","dependency_job_id":null,"html_url":"https://github.com/vaeth/quoter","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/vaeth/quoter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vaeth%2Fquoter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vaeth%2Fquoter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vaeth%2Fquoter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vaeth%2Fquoter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vaeth","download_url":"https://codeload.github.com/vaeth/quoter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vaeth%2Fquoter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272124753,"owners_count":24877720,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-25T02:00:12.092Z","response_time":1107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pipe","posix","quotes","quoting-args","shell"],"created_at":"2024-12-04T18:15:25.434Z","updated_at":"2025-08-25T20:08:18.562Z","avatar_url":"https://github.com/vaeth.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# quoter\n\nquote arguments or standard input for usage in POSIX shell by eval\n\nAuthor: Martin Väth (martin at mvath.de)\n\nThis project is under the MIT license.\nSPDX-License-Identifier: MIT\n\nThis project serves two purposes:\n\n1. Quote arguments for eval or remote usage in POSIX shells:\n`quote --` is similar to `printf '%q '` supported by some shells or\nexternal printf implementations. Note, however, that `%q` is not POSIX,\nand so one cannot rely that this works on all systems.\n\n2. Allow to deal in POSIX shell with standard input from tools which output\nstrings separated by \\0 (like GNU find -print0, but one can also achieve a\nsimilar output with POSIX find, see below)):\n\"quote -i\" is somewhat similar to \"xargs -0 printf '%q '\" except that\nneither the option -0 nor the %q are POSIX, and so one cannot rely that this\nworks on all systems.\n\nThe above points are best explained by some POSIX shell script snippets:\n\n1.\n - (i)\n   ```\n\tsu -c \"$(quoter -c -- cat -- \"$@\")\"\n   ```\n   The effect of the above snippet is similar to\n   ```\n\tsu -c \"cat -- $@\"\n   ```\n   except that the latter cannot be used if the arguments might contain\n   a special character. In fact, if `$1` has e.g. the value\n   `/dev/null; rm -rf *` then the bad code `cat -- /dev/null; rm -rf *`\n   would be executed, because the whole argument is interpreted by the\n   remote shell.\n   Note that the `--` after `quoter` in this example is mandatory to make sure\n   that quoter will not interpret any furter options.\n - (ii)\n   ```\n\tvar=$var${var:+\\ }`quoter -c -- ...`\n   ```\n   The above is the analogue of\n   ```\n\tPush var ...\n   ```\n   where Push is the function from https://github.com/vaeth/push/\n   Note that calling the external program quoter has quite some overhead, so\n   this might be slower than Push. On the other hand, it becomes faster\n   the longer and more complex the pushed data ```...``` is, because the\n   compiledcode in __quoter__ will usually do the actual quoting considerably\n   faster.\n\nThe above example 1(ii) still suffers from a problem:\nThe data which can be passed to an external program like __quoter__ is usually\nlimited by system restrictions. No such limitation exists for standard input\nand output, that is, when `quoter -i` is used.\nFor this reason, it is usually advisable to prefer `quoter -i` over the \"plain\"\nusage of `quoter`. This is simple if `printf` is a shell builtin which thus\ndoes not suffer from the mentioned system restrictions.\nIn this case, one can simply replace `quoter -- ...` by\n```\n\tprintf '%s\\0' ... | quoter -i\n```\n\nSummarizing: A variant of the above example without the mentioned limitations\n(if printf is a built-in) reads as follows:\n```\n\tvar=$var${var:+\\ }`printf '%s\\0' ... | quoter -ic`\n```\n\n2.\n  ```\n\teval \"set -- `find . -type f -print0 | quoter -i`\"\n\tfor file\n\tdo ...\n\tdone\n  ```\n  For the case that the find utility knows the GNU extension -print0,\n  this will (recursively) iterate through all ordinary files from the current\n  directory or some of its subdirectories. In contrast to the \"naive\" approach\n  ```\n\tfor file in `find . -type f`\n\tdo ...\n\tdone\n  ```\n  the above has no problem with special characters (like spaces) in filenames.\n\n  Note that POSIX find instead of GNU extensions can be used: Just replace\n  `-print0` within the command line by the more lengthy\n  ```\n  find -exec printf '%s\\0' '{}' '+'\n  ```\n  (usually the `\\` has to be quoted once more (`\\\\`), e.g. in the above\n  content).\n\nThe example 2. above still has two problems:\n\n- (a) If `find` returns nothing, then the above `eval` will expand to `set --`;\nsome (buggy) shells will not remove all arguments by this command.\nWorkaround: Add artificially an argument and remove it, that is, use instead\nof the above `eval` for instance\n```\neval \"set -- a `find . -type f -print0 | quoter -i`\"\nshift\n```\n- (b) It is hard to check whether the call to \"find\" within the above \"eval\"\nwas succesful, because POSIX returns only the exit status of the last\ncommand of a pipe, and neither -o pipefail nor the PIPESTATUS array are POSIX.\n\n\nFor this reason, this project also provides a shell function `quoter_pipe`\nTo define this shell function with __quoter-3.0__ or newer,\none can `eval` the output of the program `quoter_pipe.sh`.\nFor instance, to check whether quoter_pipe.sh is installed and to define it\nif it is, one can use something like\n```\nif SOME_VARIABLE=`quoter_pipe.sh 2\u003e/dev/null`\nthen\teval \"$SOME_VARIABLE\"\nelse\techo \"quoter_pipe.sh is not installed\" \u003e\u00262\nfi\n```\n\n__Remark__: An obsoleted method was to use instead\n```\n. quoter_pipe.sh\n```\nThe latter works for older versions of __quoter__ or if one installs manually,\nbut unless an appropriate `PATH` before sourcing is set, it fails when\n`quoter_pipe.sh` is replaced by a wrapper script which happens with the\nprovided Makefile. Moreover, if `quoter_pipe.sh` is not available,\nit stops the script.\n\nAfter the `quoter_pipe` function is defined, it can be used as follows.\nThe call\n```\n\tquoter_pipe [quoter_options] [--] command [args]\n```\nis similar to\n```\n\tquoter_pipe=`command [args] | quoter -ic [quoter_options]`\n```\nexcept that the exit status is 0 only if both commands of the pipe succeeded.\nIn addition, the variables `quoter_pipestatus` and `quoter_pipestatus1`\ncontain the exit status of the first and second command of the above pipe,\nrespectively.\n(See the comments on top of the output of `quoter_pipe.sh` for more details,\ne.g. which `quoter_options` are admissible.)\nTo do its task, `quoter_pipe` uses implementation details of `quoter` (e.g. in\nwhich way expressions actually are quoted). These implementation details might\nchange in future versions of `quoter`. Therefore, `quoter_pipe` will in general\nonly work with the version of `quoter` it is distributed with.\nFor this reason, one should make sure to have always matching versions of\n`quoter` and `quoter_pipe.sh` installed. For the same reason, it is not\nrecommended to write further scripts which rely on such implementation details.\n\nSummarizing, a variant of the above example which solves the problems (a)\nand (b) and works with POSIX `find` can look like this:\n```\n\t# first source quoter_pipe.sh from $PATH if necessary:\n\tcommand -v quoter_pipe \u003e/dev/null 2\u003e\u00261 || . quoter_pipe.sh\n\tquoter_pipe find . -type f -exec printf '%s\\0' '{}' '+' || {\n\t\t[ \"$pipestatus\" -eq 0 ] || echo \"find failed\" \u003e\u00262\n\t\t[ \"$pipestatus1\" -eq 0 ] || echo \"quote failed\" \u003e\u00262\n\t\texit 1\n\t}\n\teval \"set -- a $quoter_pipe\"\n\tshift\n\tfor file\n\tdo ...\n\tdone\n```\n\nIt is __not__ possible to specify redirection or several commands (e.g.\nseparated by `;` or `\u0026\u0026` or something similar) in the argument of\n`quoter_pipe`. If this should be needed, a function can be used:\n```\n\tmy_pipe_task() {\n\t\tsome_command \u0026\u0026 another_command \u003e/dev/null 2\u003e\u00261\n\t}\n\tquoter_pipe my_pipe_task\n```\n\nThe call `quoter -h` describes further options of `quoter`.\nSome options need a more verbose explanation:\n\nRoughly speaking, quoter is intended to be used such that\n```\n\teval \"set -- `quoter ...`\"\n```\ncan always be safely executed, i.e. all possibly \"disturbing\" characters\nare quoted or escaped. Certain non-POSIX extensions of some shells might\nrequire quoting further characters.\nFor instance, `{a,b}` is by some shells interpreted as `a` `b`, so -\nalthough not necessary according to POSIX - the symbol `{` will need to be\nescaped for such a shell.\nAny quoting which the author is aware of the currently existing popular\nshell versions (bash, dash, zsh, ksh, busybox, bosh, Bourne shell) is\ntaken care of, and if some quoting is missing, this will likely be fixed\nin a future release of __quoter__.\nThis assertion holds with and without the options `-s` and `-l`, but with\nthe option `-s`, it is attempted to reduce the quoting while with `-l` a lot\nof (usually redundant) quoting is used.\nThis means that with the option `-s`, the generated output is usually shorter\n(though usually less readable by humans) while with `-l` the generated string\nis usually longer (and less readable by humans) due to unnecessary quoting.\n\nThe default `-S` (`--unshort`) is a compromise which is readable and safe\n(for all current or obsolete shells).\nUsage of `-s` is only recommended if there is a serious reason to\nget a string which is as short as possible, e.g. if memory is an issue.\nUsage of `-l` is only recommended if paranoid future-safety is desired for\nfuture nonstandard shell extension not existing yet, e.g. when in some\nshell the symbol `%` should some day have a special meaning on the command\nline.\nUsage of `-s` and `-l` both are discouraged if a human readable output\nis desired.\n\nThe meaning of the option `--empty-last` (`-e`) is perhaps not obvious.\nIf standard input looks like this\n```\n\tstring1\\0string2\\0string3\n```\n`quoter -i` will consider the three strings `string1` `string2` `string3`\nHowever, what should happen if `string3` is empty in this case?\nIt is perhaps most logical that in this case `string3` is considered to be\nan empty string. This was indeed the case for __quoter v1.0__ and\n__quoter v1.1__.\n\nHowever, this turned out to be not very practical:\nMost commands like `find ... -print0` or `printf '%s\\0' ...` generate actually\na redundant `\\0` symbol at the end, so that the above interpretation of the\nstandard input would append a non-intended empty string as a last argument\nwhich is very disturbing and not very practical.\nFor this reason, the above is no longer the default for __quoter v2.0__\nor newer: A trailing `\\0` is no longer interpreted as\n\"an empty string follows\".\nThe purpose of the new option `--empty-last` (`-e`) is to restore the previous\n(more logical but less practical) behaviour when needed.\n\n\n## Installation\n\nJust compile the single file `src/quoter.c` (run `make` to compile it into\n`bin/quoter` with default options) and copy the result into `$PATH` under the\nname `quoter`.\nThe code is c89 compatible with the exception of two compiler specials\nwhich might optimize the code:\nIf compilation fails, try to use the compiler options\n`-DAVOID_BUILTIN_EXPECT` and/or `-DAVOID_ATTRIBUTE_NORETURN`\nto avoid these specials.\n\nTo obtain support for `quoter_pipe`, also `bin/quoter_pipe.sh` is needed in\n`$PATH` with the executable bit being set.\nTo obtain zsh completions, the content of `zsh/` is needed in zsh's `$fpath`\n(perhaps `/usr/share/zsh/site-functions/`).\nFor the standard paths, these copies all happen by `make install`.\n\nFor gentoo, there is an ebuild in the mv overlay (available by layman).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvaeth%2Fquoter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvaeth%2Fquoter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvaeth%2Fquoter/lists"}