fzf icon indicating copy to clipboard operation
fzf copied to clipboard

[Issue] Expanding {n} automatically adds quotes and character escapes

Open pidgeon777 opened this issue 3 years ago • 18 comments

  • [x] I have read through the manual page (man fzf)
  • [x] I have the latest version of fzf
  • [x] I have searched through the existing issues

Info

  • OS
    • [ ] Linux
    • [ ] Mac OS X
    • [x] Windows
    • [ ] Etc.
  • Shell
    • [ ] bash
    • [ ] zsh
    • [ ] fish
    • [x] PowerShell

Problem / Steps to reproduce

I'm using this command:

fzf --preview="bat --style=numbers --color=always {2}" --bind "ctrl-b:execute(notepad++ -n{1} {2})"

This is executed when applying Ctrl+B on a result:

notepad++ -n"12" "C:\\Work\\Test Folder\\File.txt"

I see that {2} is automatically expanded with the properly escaped path.

But {1} is causing issues. I would need to expand it without adding any quote, also without making use of any further tool such as sed/awk.

How could this be achieved in FZF?

pidgeon777 avatar Sep 07 '21 09:09 pidgeon777

Is this a problem specific to Windows environment? Because -n"12" isn't a problem on *nix shells.

i.e. There's no difference between ls -al | sort -nk"5" and ls -al | sort -nk5

junegunn avatar Sep 08 '21 04:09 junegunn

The fact is that I wish to open the FZF selected file with Notepad++. It accepts these arguments:

notepad++ -n<LINE_NUM> <FILE_PATH>

<LINE_NUM> must not include quotes ("), otherwise, it won't work. Thus, I need to output {n} without quotes.

pidgeon777 avatar Sep 08 '21 09:09 pidgeon777

Obviously, on Windows, this is not an issue strictly related to Notepad++, but potentially to every command-line application accepting arguments.

Thus, this issue should be kept into great account, in my opinion.

pidgeon777 avatar Sep 09 '21 11:09 pidgeon777

@junegunn unfortunately on Windows this "issue" makes it impossible to execute any command for example through:

type "C:\Scripts\FZF\Main.bat" | fzf --delimiter="\t" --bind "enter:execute({2})"

Main.bat content:

p    ping test.it
e    notepad++ "C:\Work\Test.txt"

When trying for example to run the e command, this will be wrongly returned by the {2} operator instead:

"notepad++ \"C:\\Work\\Test.txt\""

External quotes have been automatically added by FZF, and internal " and \ characters have been escaped. This makes it impossible to execute that command on Windows, through the FZF execute function.

All of this would be solved by not adding the external quotes and by not escaping the " and \ characters when using the {} operator. That functionality should be made optional. Or, another operator should be added which outputs the extracted string with no added quotes/escapes.

pidgeon777 avatar Sep 23 '21 08:09 pidgeon777

I'm not familiar with Windows environment. On *nix system, you would run an arbitrary command string with sh -c.

echo 'ls -al' | fzf --bind 'enter:execute:sh -c {}; sleep 1'

Maybe you can do similarly on Windows with execute(cmd /c {2})?

junegunn avatar Sep 23 '21 09:09 junegunn

I'm not familiar with Windows environment. On *nix system, you would run an arbitrary command string with sh -c.

echo 'ls -al' | fzf --bind 'enter:execute:sh -c {}; sleep 1'

Maybe you can do similarly on Windows with execute(cmd /c {2})?

Just tried but the problem, unfortunately, still occurs. The issue lies in the way the fields are automatically quoted/escaped.

Maybe a solution as follows could be implemented?

Original behaviour:
{n} - replace with field n adding quotes and escaping characters

Expanded behaviour:
[n] - replace with field n as it is
or
${n} - replace with field n as it is
or
{$n} - replace with field n as it is

The $ symbol is just an example, but my proposal could help with fixing this unwanted behaviour in Windows once for all.

What do you think about it @junegunn ?

pidgeon777 avatar Sep 23 '21 12:09 pidgeon777

fzf implements special quoting mechanism for Windows cmd.exe. You might want to check out https://github.com/junegunn/fzf/commit/c4185e81e86e339ae2c18e5d9b596b7992ec179b and see if there's room for improvement.

Have you tried execute(cmd /c {f2})?

A placeholder expression with f flag is replaced to the path of a temporary file that holds the evaluated list.

junegunn avatar Sep 23 '21 14:09 junegunn

fzf implements special quoting mechanism for Windows cmd.exe. You might want to check out c4185e8 and see if there's room for improvement.

How could I try/build it in Windows?

Have you tried execute(cmd /c {f2})?

A placeholder expression with f flag is replaced to the path of a temporary file that holds the evaluated list.

Yes, but cmd reports an error:

C:\Users\User\AppData\Local\Temp\fzf-preview-571750557

It seems as the temporary output file is missing .bat or .cmd extension, I suppose?

pidgeon777 avatar Sep 24 '21 08:09 pidgeon777

Hi, I am also interested in this. Not being able to suppress the double quotes around placeholders was first issue I noticed when starting with fzf on Windows.

I have got some observations so far:

  • ""
    • suppressing double quotes is feature request we have seen before, #1867
    • the cmd /c workaround may work for extra encompassing double quotes, but not for quotes somewhere inside the commands
  • \"
    • the problem with executing "notepad++ \"C:\\Work\\Test.txt\"" because of the \" is separate issue
      • those quotes are not added by fzf, they were in the data and that has some consequences
    • the quoteEntryCmd looks pretty solid, with one exception - it escapes double quotes with backslash, which is not very window-ish

I wrote some test cases ~~https://github.com/vovcacik/fzf/commit/67478d9219ab3ab93885b94fb36fe5f19c3a4f58~~ https://github.com/vovcacik/fzf/commit/100c86d689422b8256dc2d1c41a8ba0bd033181d

vovcacik avatar Sep 24 '21 18:09 vovcacik

I confirm everything.

\"

  • the problem with executing "notepad++ \"C:\\Work\\Test.txt\"" because of the \" is separate issue

    • those quotes are not added by fzf, they were in the data and that has some consequences

Yes, the command I wanted to run is:

notepad++ "C:\Work\Test.txt"

FZF manipulates that string in a way such that it is not possible to run the resulting command in the command prompt, as I described before.

@junegunn suggested a fix in https://github.com/junegunn/fzf/commit/c4185e81e86e339ae2c18e5d9b596b7992ec179b but I have no idea how I could compile/test it in Windows.

pidgeon777 avatar Sep 27 '21 07:09 pidgeon777

@junegunn I had some time to look at this. The problem with -n"12" is probably just on Windows, but even there you would easily find programs that can handle this type of parameters (I suppose all Go programs can parse it properly) and vice versa.

All placeholders in fzf are treated as data and are escaped for that reason. This can limit usage on both platforms (e.g., fzf generated cat '~/test' won't work, neither would cat '$HOME/test'), but it is needed, because that data is often fed to programs that need proper escaping (grep, ripgrep).

On *nix you can probably use sh -c, because it will easily understand the quotes and escaping. But cmd /c has no clue about escaped double quotes \" that fzf outputs (because it is sane thing to do with data and because ripgrep would require that kind of escaping).

But I didn't find anything in fzf that needs fixing and I am not sure if you would accept new functionality to deal with this kind of issues.

vovcacik avatar Sep 27 '21 16:09 vovcacik

@pidgeon777 try type "Main.bat" | fzf.exe --delimiter="\t" --bind "enter:execute(type {f2}|cmd)" it overcomes the lack of *.bat extension.

vovcacik avatar Sep 27 '21 16:09 vovcacik

Some ideas for future work:

  • whitelist some strings as safe and don't quote or escape them at all, e.g. ^\d+$
  • #1867 maybe went too far; what if we drop the surrounding quotes and still escape content?
  • the workaround on windows type {f}|cmd should probably be documented somewhere, equivalent to sh -c {f}
  • dropping automatic quoting means some ugliness on command line, fzf --bind "enter:execute(cmd /c \"{r2}\")" (assumes "raw" flag being implemented)
  • https://github.com/junegunn/fzf/pull/2559#discussion_r722397203 would benefit from unquoted strings

vovcacik avatar Sep 27 '21 17:09 vovcacik

I just wanted to add another Windows-specific use case which I think is related. In this case it is related to the Windows Registry which uses \ as a separator.

The command reg query HKCU returns this on the command line:

HKEY_CURRENT_USER\AppEvents
HKEY_CURRENT_USER\Console
HKEY_CURRENT_USER\Control Panel
HKEY_CURRENT_USER\Environment
HKEY_CURRENT_USER\EUDC
HKEY_CURRENT_USER\Keyboard Layout
HKEY_CURRENT_USER\Microsoft
HKEY_CURRENT_USER\Network
HKEY_CURRENT_USER\Printers
HKEY_CURRENT_USER\SOFTWARE
HKEY_CURRENT_USER\System
HKEY_CURRENT_USER\Volatile Environment

But my silly and only slightly contrived script which I think should work:

reg query HKCU | fzf --reverse --bind "enter:reload(reg query {})"

results in:

[Command failed: reg query ^"HKEY_CURRENT_USER\\AppEvents^"]

The quotes do not cause an issue, but the doubled backslashes do.

Works: reg query "HKCU\Software"

Does not work : reg query HKCU\\Software - reg reports ERROR: Invalid key name

So a way to get raw strings could be handy sometimes on Windows.

amreus avatar Jan 08 '22 16:01 amreus

Any update?

pidgeon777 avatar Mar 23 '22 15:03 pidgeon777

I second this. {n} outputs "text" on Windows and there's nothing you can do about it, which makes passing {2} to lots of programs useless (like SQLite, that treats double quoted strings as column names (there aren't any, of course)).

cpkio avatar Jul 14 '23 22:07 cpkio

@cpkio yes I confirm this limitation, hopefully this will be fixed.

pidgeon777 avatar Jul 26 '23 12:07 pidgeon777

I dont know if this will help with any particular issue, but I was able to solve my problem with cmd /c for /f %i in ({2}) do @echo select description from issues where number_in_project=%~i | sqlite3… as fzf preview command. for loop strips {2} from quotes with %~i and emits a string passed to stdin of sqlite, which luckily can accept stdin as sql command.

cpkio avatar Jul 26 '23 12:07 cpkio