lf
lf copied to clipboard
How do I execute scripts that take a choice via stdin without needing to press <enter>?
I am trying to create a trash
command that mimics the built-in delete
command by doing the following:
- Prompt the user:
trash 'filename' ? [y/N]
- User presses a key without needing to press
<enter>
. - Actions are performed accordingly.
So far, I have written this:
# Move the file/directory to trash.
cmd trash %{{
set -f
if ! command -v trash-put >/dev/null 2>&1; then
echo "trash-cli is not installed."
exit 1
fi
file_count=$(echo "$fx" | wc -l)
if [ $file_count -eq 1 ]; then
printf "trash '%s'?" $fx
else
printf "trash %s items?" $file_count
fi
printf " [y/N] "
choice=$(/usr/bin/env bash -c 'read -n 1 choice; echo $choice')
# Clear the prompt.
echo
if [ $choice != "y" ]; then
exit
fi
trash-put $fx
}}
The script behaves well if I execute in my dash
shell interactively. However, the input requires me to press <enter>
if I invoke the command via lf
. What could be the issue here?
Secondly, is there any way I can suppress the ">" symbol that appears for shell-pipe
commands for this command specifically. It's not a high priority thing, but would be nice to know if I could.
Thanks.
Replace %{{
with ${{
. Why do you even want a shell-pipe for such an interactive use case?
Btw, you could just read -r -n 1 choice
instead of choice=$(.....)
.
I want the script output to be shown at the bottom. Using ${{
completely occupies the whole screen. Also, I am using the choice=$(...)
because /bin/sh
points to dash
which strictly conforms to the POSIX standard.
In POSIX, read -n 1
does not exist.
I have a weird idea that could be a last resort if nobody has better ideas:
cmd delete :{{
map y send_key_y
map n send_key_n
trashing_listener
}}
cmd send_key_y ${{
pipe="/tmp/my_pipe_$id"
echo 'y' > "$pipe"
}}
cmd send_key_n ${{
pipe="/tmp/my_pipe_$id"
echo 'n' > "$pipe"
}}
cmd trashing_listener &{{
pipe="/tmp/my_pipe_$id"
mkfifo "$pipe"
cleanup() {
rm -f "$pipe"
lf -remote "send $id remap"
lf -remote "send $id echo"
exit 0
}
if ! command -v trash-put >/dev/null 2>&1; then
lf -remote "send $id echo \"trash-cli is not installed.\""
# exit 1
fi
file_count=$(echo "$fx" | wc -l)
if [ $file_count -eq 1 ]; then
lf -remote "send $id echo \"trash $fx? [y/N]\""
else
lf -remote "send $id echo \"trash $file_count items? [y/N]\""
fi
while true; do
if read -r msg < "$pipe"; then
if [ "$msg" = "y" ]; then
notify-send "trash-put $fx"
fi
cleanup
fi
done
}}
cmd remap :{{
map y copy
}}
delete
calls a process that will listen for input and remaps y
and n
to provide that input
trashing_listener
creates a loop that listens for y
remap
is supposed to return key mappings to the original state
Years ago in 2020 when I was configuring lf, I wanted the exact same thing you want. Back then, it was achievable by setting the shell to zsh
and running read -q
inside %{{ ... }}
. See my dotfiles: https://github.com/randoragon/dotfiles/blob/cfcc10ff35e2f8d4633b035aa0bcf9bc7787c641/lf/.config/lf/lfrc#L34
However, a few years ago, I don't remember when, it stopped to work. Now read -q
hangs inside %{{ }}
, and only works properly inside ${{ }}
which, as you pointed out, is inconsistent with :delete
, because it occupies the entire screen.
I think this may be a bug, but I've been too lazy to investigate or open my own ticket :P
@UtkarshVerma @randoragon If you are willing to deal with tmux flow, you can try to do it like:
cmd trash ${{
split_height=$(( $(echo "$fx" | wc -l) + 2 ))
[ $split_height -gt $lf_height ] && split_height=$lf_height
tmux split-window -l $split_height '
echo -n "'"$fx"'\n\nDelete files? [y/n] "
stty -icanon -echo
choice=$(dd bs=1 count=1 2>/dev/null)
if [[ "$choice" == "y" ]]; then
while IFS= read -r file; do
notify-send "Moving file to trash..." ">$file<"
done <<< "'"$fx"'"
lf -remote "send '$id' load"
fi
'
}}