hyperfine icon indicating copy to clipboard operation
hyperfine copied to clipboard

Bug ~ quoting? failure on windows-platform

Open rivy opened this issue 4 years ago • 8 comments

In example, for bat:

C:\>bat.exe --paging=always --pager="cmd.exe /c more.com" README.md
       │ File: README.md
   1   │ uutils coreutils
   2   │ ================
   3   │
   4   │ [![Discord](https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat)](https://discord.
       │ gg/wQVJbvJ)
   5   │ [![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/coreutils/blob/master/LICENSE)
   6   │ [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fuutils%2Fcoreutils.svg?type=shield)](https://app.fos
       │ sa.io/projects/git%2Bgithub.com%2Fuutils%2Fcoreutils?ref=badge_shield)
   7   │ [![LOC](https://tokei.rs/b1/github/uutils/coreutils?category=code)](https://github.com/Aaronepower/tokei)
   8   │ [![dependency status](https://deps.rs/repo/github/uutils/coreutils/status.svg)](https://deps.rs/repo/github/uutils/coreuti
       │ ls)
   9   │
  10   │ [![Build Status](https://api.travis-ci.org/uutils/coreutils.svg?branch=master)](https://travis-ci.org/uutils/coreutils)
  11   │ [![Build Status (Windows)](https://ci.appveyor.com/api/projects/status/787ltcxgy86r20le?svg=true)](https://ci.appveyor.com
       │ /project/Arcterus/coreutils)
  12   │ [![Build Status (FreeBSD)](https://api.cirrus-ci.com/github/uutils/coreutils.svg)](https://cirrus-ci.com/github/uutils/cor
       │ eutils/master)

When testing with hyperfine:

C:\>hyperfine "bat.exe --paging=always --pager=\"cmd.exe /c more.com\" README.md"
Benchmark #1: bat.exe --paging=always --pager="cmd.exe /c more.com" README.md                                                     0
Error: Command terminated with non-zero exit code. Use the '-i'/'--ignore-failure' option if you want to ignore this. Alternatively, use the '--show-output' option to debug what went wrong.

but, using a command-string without internal quoting (by passing arguments to bat via the environment):

env BAT_PAGER="cmd.exe /c more.com" hyperfine "bat.exe --paging=always README.md"
Benchmark #1: bat.exe --paging=always README.md                                                                                   0
  Time (mean ± σ):     199.9 ms ±  51.9 ms    [User: 10.3 ms, System: 6.8 ms]                                                      0
  Range (min … max):   179.8 ms … 347.5 ms    10 runs

  Warning: The first benchmarking run for this command was significantly slower than the rest (347.5 ms). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.

The text of the target command shown by hyperfine looks correct, but the target is not being executed as displayed.

This is likely a shell quoting issue.

rivy avatar Jun 02 '20 00:06 rivy

Thank you for reporting this.

So what are the actual quoting/escaping rules for cmd.exe? I seem to remember that they are highly non-intuitive (for a Unix user). Can

"bat.exe --paging=always --pager=\"cmd.exe /c more.com\" README.md"

be passed to other programs without any problem?

sharkdp avatar Jun 05 '20 21:06 sharkdp

You're correct; parsing command lines on Windows is complicated and quirky.

And the rules changed in 2008.

David Deley has a good explanation of the various rules and changes (with examples) "How Command Line Parameters Are Parsed".

If you're handing the command line off to cmd, you should be able to just mechanically escape every character with ^ (eg, cmd.exe /d/c ^e^c^h^o ^"^t^h^i^s^ ^>^ ^w^o^r^k^s^").

If you're handing off directly to another windows process, I think it's ok to assume the 2008+ parsing behavior, except that rust is currently using the older, pre-2008, parsing behavior (which is extra "quirky"). But it does look like that is likely to change. I'm sure there must be mechanical translation recipe that would work for the direct process approach, but I haven't been able to create or find it.

Command is struggling with this as well.

That's why I fell back to cmd.exe /d/c ... for env in coreutils.

rivy avatar Jun 07 '20 23:06 rivy

Thank you for the detailed explanation and for all the references, especially "How Command Line Parameters Are Parsed". I didn't know it was that complicated :frowning:

I'm inclined to accept the cmd.exe /d/c ^…^… workaround for hyperfine, if you think that this would solve most issues.

Alternatively, would it help to use PowerShell instead of cmd.exe? This could be easily tested via hyperfines --shell option (if PowerShell also uses the syntax /c <cmd>).

sharkdp avatar Jun 08 '20 17:06 sharkdp

Alternatively, would it help to use PowerShell instead of cmd.exe? This could be easily tested via hyperfines --shell option (if PowerShell also uses the syntax /c <cmd>).

I don't think PowerShell is a solution at this point. The initial design was not well thought out and it's also struggling with command line escaping and still evolving.

cmd.exe is, in comparison, a known, relatively stable, problem space.

rivy avatar Jun 09 '20 04:06 rivy

It looks like the resolution of https://github.com/rust-lang/rust/issues/29494 (a new raw_arg method on Windows) could help us resolve this issue. It's currently only available on nightly though.

sharkdp avatar Jul 27 '21 21:07 sharkdp

It looks like the resolution of rust-lang/rust#29494 (a new raw_arg method on Windows) could help us resolve this issue. It's currently only available on nightly though.

👍🏻 I've been keeping an eye on that issue as well.

rivy avatar Jul 28 '21 03:07 rivy

Any update on this?

ofek avatar Nov 14 '21 22:11 ofek

Looks like this might be stabilized in 1.61, so it will take a few more months (at least).

In the meantime, you might be able to use the new -N/--shell=none option of hyperfine to disable the shell completely.

sharkdp avatar Mar 05 '22 12:03 sharkdp

FYI @rivy

raw_arg has been stabilized for a while now: https://doc.rust-lang.org/std/os/windows/process/trait.CommandExt.html#tymethod.raw_arg

Someone with access to a Windows machine might be able to resolve this, ideally backed up by some new (regression) tests.

sharkdp avatar Apr 24 '23 15:04 sharkdp

Hello, I can take a shot a this. I've being meaning to get more into OSS and rust, and I run mostly on windows, specially at work.

Pretty cool tool, btw. It's my go-to for performance tests 🥇

PedroWitzel avatar Aug 28 '23 20:08 PedroWitzel