Skip to content
This repository was archived by the owner on Dec 29, 2021. It is now read-only.

echo "42" #15

Closed
colin-kiegel opened this issue Mar 20, 2017 · 6 comments
Closed

echo "42" #15

colin-kiegel opened this issue Mar 20, 2017 · 6 comments
Labels

Comments

@colin-kiegel
Copy link
Collaborator

when I run echo "42" on my shell it prints 42, just as echo 42 does.

However

assert_cmd!(echo "42").succeeds().prints("\"42\"").unwrap();

Assert::command(&["echo", "\"42\""]) behaves identical.

I would have naively expected the behaviour from my shell. But I don't know how other shell APIs behave. Is there any particular reason for the current behaviour?

PS: I'll get to the code too .. I'm just familiarizing myself with this crate first. :-)

@killercup
Copy link
Collaborator

Hm, assert_cmd! uses stringify! internally, maybe that also cause this to be weird? I think I have to rethink that macro, or try to extend it to be a bit more clever (like not stringifying exprs).

@killercup killercup added the bug label Mar 20, 2017
@colin-kiegel
Copy link
Collaborator Author

colin-kiegel commented Mar 20, 2017

I suspect this happens on the expansion side, since Assert::command(&["echo", "\"42\""]) behaves identical (I would naively expect this to correspond to echo "42").

The question is how Assert::command(&["foo", "bar"]) is supposed to expand. Does it expand to foo bar or does it expand to foo "bar"? I may have a wrong picture of expansion or correspondence to shell commands though. If arguments are supposed to contain spaces &["foo", "bar with space baz"], then enclosing them in "..." would be necessary.

Btw. I just double-checked the behaviour of bash-eval ..

$ FOO=foo && eval echo $FOO // ellision of quotes in definition and argument
foo
$ FOO="foo" && eval echo $FOO // ellision of quotes in argument
foo
$ FOO="\"foo\"" && eval echo $FOO // no ellision of quotes, i.e. `echo "foo"`
foo
$ FOO="\"\\\"foo\\\"\"" && eval echo $FOO // now we get to `echo "\"foo\""`
"foo"

.. oh dear, I love bash. (not really). LOL.

@killercup
Copy link
Collaborator

killercup commented Mar 20, 2017

POSIX shell syntax is basically a lisp where everything is a string. 😅

So, echo 42 (or echo "42" for that matter) is the same as "echo" "42" (try it!). Using quotes allows you to have arguments with white space in them, like cat "file with a fancy name.md". echo is a bad example here, as it, well, echos all arguments (have fun with "$*"!).

But, we are not in a shell here! Rust's std::process::Command spawns a new process without any shell interpolation by just calling execvp with an array of CStrings here.

So, for us, there is a difference between ["echo", "42"] and ["echo", "\"42\""], and I really need to make sure it's either a meaningful one or not a surprising one :)

@colin-kiegel
Copy link
Collaborator Author

Hm. Ok I didn't know much about LISP but now I don't want to learn it either. ^^

Regarding the initial "problem" I think it can be solved via documenting the status quo. That is assert_cmd!(foo bar baz) corresponds to the in-shell command "foo" "bar" "baz".

This means however that the assert_cmd macro would be strictly less expressive than the Assert::command function, which is a pity. But you can't express the in-shell command git commit -m "lorem ipsum" with the current macro. The best you'll get is either "git" "commit" "-m" "lorem" "ipsum" or "git" "commit" "-m" "\"lorem ipsum\"". Documenting this as status quo would be an option at least...

@colin-kiegel
Copy link
Collaborator Author

colin-kiegel commented Mar 21, 2017

I made a comment in a commit. That was probably not my best idea today.. ^^

@colin-kiegel
Copy link
Collaborator Author

TL;DR: rustc_serialize::json::Json::from_str(stringify!("foo")); yields "foo". :-)


Ok, we can detect if the stringified output is delimited by ". In that case we now, that we have stringified a string. How do we un-stringify?

That should be the same as to deserialize a string, like in JSON, right? Ok, now phrased that way it sounds solvable.

These crates should be able to handle that job:

I haven't used any of these, but rustc-serialize seems more lightweight than serde_json, so I gave it a try, and voila:

extern crate rustc_serialize;
use rustc_serialize::json::Json;

fn main() {
    let x = stringify!("foo");
    if let Ok(Json::String(y)) = Json::from_str(x) {
        println!("x={:?}, y={:?}", x, y);
        // ^--> x="\"foo\"", y="foo"
    }
}

PS:

  • String implements FromStr, but that's only a clone. Too bad.
  • I also found shlex, which sounds related. But I don't get what it does.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants