lf
lf copied to clipboard
Problems with shell commands and lfrc parsing
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:
- No builtin way of using startup files
-
shellopts
parsing is broken for multiple arguments, even though it is said to accept[]string
in the documentation - Side effects for setting zsh as shell
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.
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.
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
- your aliases, functions, ... basically whatever your real .zshrc contains
- the literal command to run
-
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.
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.
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.
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.