Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] Allow adjustment of output formatting #4319

Open
3 of 10 tasks
ferguscollins opened this issue Mar 21, 2025 · 5 comments
Open
3 of 10 tasks

[Feat] Allow adjustment of output formatting #4319

ferguscollins opened this issue Mar 21, 2025 · 5 comments
Labels

Comments

@ferguscollins
Copy link

ferguscollins commented Mar 21, 2025

Checklist

  • I have read through the manual page (man fzf)
  • I have searched through the existing issues
  • For bug reports, I have checked if the bug is reproducible in the latest version of fzf

Output of fzf --version

0.60.3 (0012183)

OS

  • Linux
  • macOS
  • Windows
  • Etc.

Shell

  • bash
  • zsh
  • fish

Problem / Steps to reproduce

Right now using {n} outputs the field in double quotes. In nix I can easily trim this but in Windows this becomes a hassle and requires post processing of the fzf output outside of the fzf call itself.

Could an option be added to define what the wrapping character is? Or just a toggle to remove it?

Example:

$ type file.csv
test

$ type file.csv | fzf --bind "enter:accept"
test

$ type file.csv | fzf --bind "enter:execute(echo {})+abort"
"test"

This is a minimum example to showcase the output difference, I am aware that the function in the example is no different to the regular fzf output.

@junegunn
Copy link
Owner

Have you tested --accept-nth?

@ferguscollins
Copy link
Author

I have, it does not change the quote wrapping between accept and echoing the output.

@junegunn
Copy link
Owner

Can you test again? --accept-nth doesn't produce quotes.

$ echo foo bar | fzf --accept-nth 2
bar

@ferguscollins
Copy link
Author

Apologies I wasn't clear in which part produces quotes, I am specifically talking about using {n} to output either part or all of the selected row.

$ echo test | fzf --accept-nth 1
test

$ echo test | fzf --accept-nth 1 --bind "enter:execute(echo {1})+abort"
"test"

$ echo test | fzf --accept-nth 1 --bind "enter:execute(echo {})+abort"
"test"

This is always formatted differently to the output of accept and needs to be handled differently between accept and {} if the quotes need to be trimmed (apologies for the word vomit of a sentence, I am not sure how else to describe the caveat). If multiple combinations need to be selectable then using --accept-nth is not an option as it can only be used for a single combination.

In the below examples, selecting a value with enter will yield the first two fields formatted without quotes, and selecting with f1 or f2 will yield the first and last fields with different kinds of quotation. In a single call of fzf the logical option is to never use --accept-nth if multiple output options are required as then you will be forced to account for different kinds of output formatting as opposed to only having to deal with quotes when using {n}.

$ echo foo bar baz | fzf --accept-nth 1,2 
foo bar

$ echo foo bar baz | fzf --bind "f1:execute(echo {1,3})+abort"
"foo baz"

$ echo foo bar baz | fzf --bind "f2:execute(echo {1} {3})+abort"
"foo" "baz"

On Linux this isn't a problem as the formatting of {n} and accept are the same. All of my post processing scripts that rely on consistent output formatting would need to have handling added for use in Windows, it makes portability between OSes more difficult.

I would argue that all of the outputs should be formatted consistently, and that that format should be without quotes for all options as it is when using fzf on Linux. This may impact legacy implementations on Windows however hence the feature request instead of a bug report.

I should have included all of this detail in the initial post, I realise it is not as clear as it was in my head.

@junegunn
Copy link
Owner

We need to clarify a few things.


A placeholder expression is designed to be evaluated to a quoted string, so that it can be safely passed as an argument to an external program. It was first introduced to be used with --preview option.

fzf --preview 'cat {}'

However, it looks like echo of Windows, unlike in Linux, behaves differently and does not recognize single quotes as quoting characters. So I guess this automatic quoting may not be desirable on Windows. Related #4330.


Another thing.

echo test | fzf --accept-nth 1 --bind "enter:execute(echo {1})+abort"

FWIW, this strategy will not work on non-Windows binaries since 0.53.0.

  • Process started by execute action now directly writes to and reads from /dev/tty. Manual /dev/tty redirection for interactive programs is no longer required.
    # Vim will work fine without /dev/tty redirection
    ls | fzf --bind 'space:execute:vim {}' > selected

The output of execute is directed to /dev/tty and you can't capture it.

echo test | fzf --bind 'enter:execute(echo {1})+abort' | wc
# test
#        0       0       0

You're supposed to use become action instead.

echo test | fzf --bind "enter:become(echo {1})" | wc
       1       1       5

become also works on Windows, so you should use it instead of the old execute way.


If multiple combinations need to be selectable then using --accept-nth is not an option as it can only be used for a single combination.

You can specify a template if you need multiple placeholders.

echo foo bar baz | fzf --accept-nth '[template example] {1} // {1,3,2} // {1} {3} {2} // {-1}'
    # [template example] foo // foo bazbar // foo baz bar // baz
  • Note that the output of each placeholder expression is not quoted. That's because when you use --accept-nth, it's not executing an external program, but it's just expanding the template string. So no quoting for argument passing.
  • Also note that {1,3,2} and {1} {3} {2} give different results.
  • You can read more about the template in https://junegunn.github.io/fzf/releases/0.60.0/

format should be without quotes for all options as it is when using fzf on Linux

As noted above, each placeholder expression is "always" quoted and properly escaped to be passed as an argument on Linux. Say you have a file named it's mine and you want to preview the file. If {} isn't quoted, it's unnecessarily tricky to handle it correctly.

fzf --preview 'cat {}'

becomes cat it's mine, and you'll get unexpected EOF while looking for matching ' error.

Manual quoting doesn't work either.

fzf --preview "cat '{}'"

becomes cat 'it's mine', and you get the same error. So, fzf quotes it for you, escaping single quotes in it, so cat {} "just works".


fzf also does quoting on Windows, but using a different set of rules.

func (x *Executor) QuoteEntry(entry string) string {
switch x.shellType {
case shellTypeCmd:
/* Manually tested with the following commands:
fzf --preview "echo {}"
fzf --preview "type {}"
echo .git\refs\| fzf --preview "dir {}"
echo .git\refs\\| fzf --preview "dir {}"
echo .git\refs\\\| fzf --preview "dir {}"
reg query HKCU | fzf --reverse --bind "enter:reload(reg query {})"
fzf --disabled --preview "echo {q} {n} {}" --query "&|<>()@^%!"
fd -H --no-ignore -td -d 4 | fzf --preview "dir {}"
fd -H --no-ignore -td -d 4 | fzf --preview "eza {}" --preview-window up
fd -H --no-ignore -td -d 4 | fzf --preview "eza --color=always --tree --level=3 --icons=always {}"
fd -H --no-ignore -td -d 4 | fzf --preview ".\eza.exe --color=always --tree --level=3 --icons=always {}" --with-shell "powershell -NoProfile -Command"
*/
return escapeArg(entry)
case shellTypePowerShell:
escaped := strings.ReplaceAll(entry, `"`, `\"`)
return "'" + strings.ReplaceAll(escaped, "'", "''") + "'"
default:
return "'" + strings.ReplaceAll(entry, "'", "'\\''") + "'"
}
}

Coming up with the rule was painful due to many edge cases, but I guess it's still not perfect. See #2609 and #3651.

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

No branches or pull requests

2 participants