lf icon indicating copy to clipboard operation
lf copied to clipboard

Problems with shell commands and lfrc parsing

Open kazmath opened this issue 2 years ago • 6 comments

I have a few startup files that set the environment for when I use shell. Those files are sourced in whatever shell I'm using at the moment ($HOME/.zshrc for zsh and $HOME/.bashrc for bash), but lf does not go through those files at all.

At first I though it was because sh (the default shell for lf) apparently doesn't have startup files, so I changed the shell to bash instead, but even that doesn't appear work, since I have aliased vim to nvim and that doesn't work in lf.

Another thing I tried was using --init-file argument in bash to forcibly set a startup file, like so:

set shellopts       '--init-file':'~/.bashrc':'-eu'

But that gives an error at startup (unexpected token: , probably saying the lfrc couldn't be parsed) and whenever I run a command (running shell: exit status 127 at lf and bash: IFS='<newline>'; echo foobar: No such file or directory at stdout)

Another problem I have is the fact that using zsh as shell throws an error at startup sometimes, but I can use bash anyways and I don't know the exact conditions for it to happen. I also Just thought I should include it since it's most likely also a problem with the parsing. Also, the reason I know it has something to do with setting zsh as shell is because when I changed it to default (sh) without changing anything it worked, although now (after some edits to the config) zsh also works somehow.

So, summarizing my issues:

  1. No builtin way of using startup files
  2. shellopts parsing is broken for multiple arguments, even though it is said to accept []string in the documentation
  3. Side effects for setting zsh as shell

kazmath avatar Jan 27 '23 04:01 kazmath

lf is not supposed to load your shell's configuration file. lf inherits the environment from the shell in which its run, as every other process started in a shell does.

Do a quick test: export FOO=bar, type lf and run !echo $FOO to check whether that basic mechanism works as expected. If not, some part of the configuration is messed up and we'll go from here.

slavistan avatar Apr 08 '23 11:04 slavistan

Environment variables do still work, yes, but my main problem are aliases. I use nvim as my editor and my vim is not configured correctly at all, so I aliased 'vim' to 'nvim' in my startup (along with some other things like default arguments for xclip, rm, etc), but lf doesn't recognize any of those (vim opens the vim executable and which vim returns the path to it instead of the alias). Functions also do not work, I did try using export on them in case that worked somehow, but no.

kazmath avatar Apr 08 '23 20:04 kazmath

To have aliases and functions you can define a dummy custom shell to bootstrap a zsh instance for the sole purpose of running your command. Instead of using a commandline flag to source a custom .zshrc, zsh offers $ZDOTDIR, which will be used to search for .zshrc. Using this, you can just start a regular interactive zsh and give it a faux .zshrc which contains

  1. your aliases, functions, ... basically whatever your real .zshrc contains
  2. the literal command to run
  3. exit to quit after the command has run

Here's an example. Name the file lfzsh, make it executable and put it in your $PATH.

#!/usr/bin/env zsh

argv=($@[2,-2]) # removes leading '-c' and trailing '--' from argument list passed by lf
dummy_zdotdir="$(mktemp -d)"
dummy_zshrc="$dummy_zdotdir/.zshrc"

cat >"$dummy_zshrc" <<<'

# 1. aliases, functions
alias vim=nvim
bar() {
	echo "$@"
}

# 2. the command to run
'"$argv"'

# 3. make the shell quit after our command has finished.
exit'

ZDOTDIR="$dummy_zdotdir" zsh
rm -r "$dummy_zdotdir"

That'll be the dummy shell we're going to configure lf to use.

# lfrc
set shell lfzsh

With that you have the vim alias and the bar function available within lf. I've used a herestring to create that dummy .zshrc, whereas you'll probably want to load your existing (real) .zshrc which contains your aliases and functions.

slavistan avatar Apr 09 '23 06:04 slavistan

That's just so hacky.... (Also, you'd probably want to split the args $@[3,-2] instead because of "shellopts".) Wouldn't they want to add a builtin way of doing it instead of not only polluting the PATH (which can be avoided if you pass another folder through env while calling lf), but also needing to create and delete a temp folder everytime a command is called, potentially making it slower in machines without an SSD.

Loading your own .zshrc would be hard too, since you still need to handle multiple commands separated by ; and the -c flag doesn't load the rc file. Something like this wouldn't work, for example:

#!/usr/bin/env zsh

set $@[1]
argv=($@[3,-2])

source $ZDOTDIR/.zshrc
$argv
exit

Because $argv expands to a single command and doesn't recognize ; as a token, but as an argument instead (';').

There might be another way, but I still find it hacky so some optimal ways of going about it is by either creating a new option called rcfile or something, or creating a new builtin function that can be overridden (like open or on-cd) which gets called first everytime the shell is spawned.

kazmath avatar Apr 10 '23 01:04 kazmath

I agree, it is hacky, but that's because what we're asking zsh to do is some weird superposition of doing things in interactive mode (user-specific config) and executing a shell script (running commands and exiting). This is a fringe use-case and does not warrant adding to the complexity to zsh, if you ask me.

With regards to your attempts to use a custom rcfile with bash, if you remove the quotes your shellopts will be parsed correctly.

set shellopts --init-file:~/.bashrc:-eu

However, --init-file is used for interactive shells only, according to my man bash. bash --init-file ~/.bashrc -c foo does not recognize the foo alias defined in my bashrc.

slavistan avatar Apr 10 '23 08:04 slavistan

I see, I guess I didn't bother to confirm if --init-file worked normally for non-interactive calls, sorry for that. I can roughly understand how the shellopts option works now as well, as it concatenates the []string array after individually resolving what each unescaped character does, instead of leaving that job to the shell (although it is mostly undocumented that is does that). So that solves the problem 2. in my original issue.

Problem 3. is unreproducible, so let's say it's solved. (Honestly, that was probably also a problem with the parsing of shellopts)

I still think adding a option that calls a script or function that's executed before every command could be good, even if it's a bit of an edge-case. Is that actually difficult to add compared to builtin commands like on-cd, on-select and on-paste? Wouldn't you only need to concatenate it with the command being executed? I can't read GO, so I don't know.

kazmath avatar Apr 16 '23 02:04 kazmath