forked from lix-project/lix
Merge pull request #8470 from ncfavier/shebang-single-quotes
nix-shell: support single quotes in shebangs, fix whitespace parsing
(cherry picked from commit 3b99c6291377cbd22607896af9dfafa857d2f2dc)
Change-Id: I2a431b21c3467eefa1ef95d5a36d672f45b6937a
This commit is contained in:
parent
7f590ea709
commit
032eff7f69
|
@ -235,14 +235,14 @@ package like Terraform:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#! /usr/bin/env nix-shell
|
#! /usr/bin/env nix-shell
|
||||||
#! nix-shell -i bash --packages "terraform.withPlugins (plugins: [ plugins.openstack ])"
|
#! nix-shell -i bash --packages 'terraform.withPlugins (plugins: [ plugins.openstack ])'
|
||||||
|
|
||||||
terraform apply
|
terraform apply
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
> You must use double quotes (`"`) when passing a simple Nix expression
|
> You must use single or double quotes (`'`, `"`) when passing a simple Nix expression
|
||||||
> in a nix-shell shebang.
|
> in a nix-shell shebang.
|
||||||
|
|
||||||
Finally, using the merging of multiple nix-shell shebangs the following
|
Finally, using the merging of multiple nix-shell shebangs the following
|
||||||
|
@ -251,7 +251,7 @@ branch):
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
#! /usr/bin/env nix-shell
|
#! /usr/bin/env nix-shell
|
||||||
#! nix-shell -i runghc --packages "haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])"
|
#! nix-shell -i runghc --packages 'haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])'
|
||||||
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-20.03.tar.gz
|
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-20.03.tar.gz
|
||||||
|
|
||||||
import Network.Curl.Download
|
import Network.Curl.Download
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
# Release X.Y (202?-??-??)
|
# Release X.Y (202?-??-??)
|
||||||
|
|
||||||
|
- `nix-shell` shebang lines now support single-quoted arguments.
|
||||||
|
|
|
@ -34,13 +34,14 @@ extern char * * environ __attribute__((weak));
|
||||||
*/
|
*/
|
||||||
static std::vector<std::string> shellwords(const std::string & s)
|
static std::vector<std::string> shellwords(const std::string & s)
|
||||||
{
|
{
|
||||||
std::regex whitespace("^(\\s+).*");
|
std::regex whitespace("^\\s+");
|
||||||
auto begin = s.cbegin();
|
auto begin = s.cbegin();
|
||||||
std::vector<std::string> res;
|
std::vector<std::string> res;
|
||||||
std::string cur;
|
std::string cur;
|
||||||
enum state {
|
enum state {
|
||||||
sBegin,
|
sBegin,
|
||||||
sQuote
|
sSingleQuote,
|
||||||
|
sDoubleQuote
|
||||||
};
|
};
|
||||||
state st = sBegin;
|
state st = sBegin;
|
||||||
auto it = begin;
|
auto it = begin;
|
||||||
|
@ -50,26 +51,39 @@ static std::vector<std::string> shellwords(const std::string & s)
|
||||||
if (regex_search(it, s.cend(), match, whitespace)) {
|
if (regex_search(it, s.cend(), match, whitespace)) {
|
||||||
cur.append(begin, it);
|
cur.append(begin, it);
|
||||||
res.push_back(cur);
|
res.push_back(cur);
|
||||||
cur.clear();
|
it = match[0].second;
|
||||||
it = match[1].second;
|
if (it == s.cend()) return res;
|
||||||
begin = it;
|
begin = it;
|
||||||
|
cur.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (*it) {
|
switch (*it) {
|
||||||
case '"':
|
case '\'':
|
||||||
|
if (st != sDoubleQuote) {
|
||||||
cur.append(begin, it);
|
cur.append(begin, it);
|
||||||
begin = it + 1;
|
begin = it + 1;
|
||||||
st = st == sBegin ? sQuote : sBegin;
|
st = st == sBegin ? sSingleQuote : sBegin;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
if (st != sSingleQuote) {
|
||||||
|
cur.append(begin, it);
|
||||||
|
begin = it + 1;
|
||||||
|
st = st == sBegin ? sDoubleQuote : sBegin;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case '\\':
|
case '\\':
|
||||||
|
if (st != sSingleQuote) {
|
||||||
/* perl shellwords mostly just treats the next char as part of the string with no special processing */
|
/* perl shellwords mostly just treats the next char as part of the string with no special processing */
|
||||||
cur.append(begin, it);
|
cur.append(begin, it);
|
||||||
begin = ++it;
|
begin = ++it;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (st != sBegin) throw Error("unterminated quote in shebang line");
|
||||||
cur.append(begin, it);
|
cur.append(begin, it);
|
||||||
if (!cur.empty()) res.push_back(cur);
|
res.push_back(cur);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +142,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
for (auto line : lines) {
|
for (auto line : lines) {
|
||||||
line = chomp(line);
|
line = chomp(line);
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell (.*)$")))
|
if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell\\s+(.*)$")))
|
||||||
for (const auto & word : shellwords(match[1].str()))
|
for (const auto & word : shellwords(match[1].str()))
|
||||||
args.push_back(word);
|
args.push_back(word);
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,11 @@ chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb
|
||||||
output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.rb abc ruby)
|
output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.rb abc ruby)
|
||||||
[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/spaced \'\''"shell.shebang.rb abc ruby' ]
|
[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/spaced \'\''"shell.shebang.rb abc ruby' ]
|
||||||
|
|
||||||
|
# Test nix-shell shebang quoting
|
||||||
|
sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.nix > $TEST_ROOT/shell.shebang.nix
|
||||||
|
chmod a+rx $TEST_ROOT/shell.shebang.nix
|
||||||
|
$TEST_ROOT/shell.shebang.nix
|
||||||
|
|
||||||
# Test 'nix develop'.
|
# Test 'nix develop'.
|
||||||
nix develop -f "$shellDotNix" shellDrv -c bash -c '[[ -n $stdenv ]]'
|
nix develop -f "$shellDotNix" shellDrv -c bash -c '[[ -n $stdenv ]]'
|
||||||
|
|
||||||
|
|
10
tests/functional/shell.shebang.nix
Executable file
10
tests/functional/shell.shebang.nix
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#! @ENV_PROG@ nix-shell
|
||||||
|
#! nix-shell -I nixpkgs=shell.nix --no-substitute
|
||||||
|
#! nix-shell --argstr s1 'foo "bar" \baz'"'"'qux' --argstr s2 "foo 'bar' \"\baz" --argstr s3 \foo\ bar\'baz --argstr s4 ''
|
||||||
|
#! nix-shell shell.shebang.nix --command true
|
||||||
|
{ s1, s2, s3, s4 }:
|
||||||
|
assert s1 == ''foo "bar" \baz'qux'';
|
||||||
|
assert s2 == "foo 'bar' \"baz";
|
||||||
|
assert s3 == "foo bar'baz";
|
||||||
|
assert s4 == "";
|
||||||
|
(import <nixpkgs> {}).runCommand "nix-shell" {} ""
|
Loading…
Reference in a new issue