Reputation: 43447
I have a simple shell alias
alias fz='vim -p $(fzf -m)'
fzf is an interactive terminal program, which only when it terminates sends a list of filenames on stdout. This then is used to open the files chosen with vim.
Now the trouble is when i decide to cancel and hit ctrl+c, the shell goes right ahead and still opens vim (as if I just ran vim -p
). This kind of makes sense.
Now, one resolution to the immediate problem that I have is to simply make my alias more sophisticated so that it does not launch vim if fzf's output is empty. Alternatively, I could likely do something to abort launching vim if fzf's exit code is not zero.
However, I am curious about how I might go about commanding my shell to terminate this fz
when I Ctrl + C. Is it simply not possible when I am in the subshell context? Would I be able to do so by setting process group id somehow?
I tried set -m
, but it did not change behavior.
Upvotes: 4
Views: 1320
Reputation: 71
I took a similar approach inspired by Gabriel's answer, but modified to be simpler and dynamic to the editor that is configured as default. Note that I have adapted it to be used for opening single files and not multiple, but that's easily changed:
fzfedit() {
local sel="$(fzf)"
# If the selected file exists, open it with $EDITOR
[[ -e "$sel" ]] && $EDITOR "$sel"
}
Or even more concisely as an alias:
alias fzfedit='sel=$(fzf) && test -e "$sel" && $EDITOR "$sel"'
Upvotes: 0
Reputation: 52817
fzf
(or any other command) exit when you exit early with Ctrl + CThis works:
# command
files_list=$(fzf -m) && vim -p $files_list
# alias
alias fz='$(fzf -m) && vim -p $files_list'
Explanation:
When fzf
exits normally it returns error code 0
(note: you can read the return code after it exits by running echo $?
). When fzf
is killed by Ctrl + C, however, it returns error code 130
. The &&
part means "only do the part on the right if the part on the left is zero"--meaning that the command on the left completed successfully. So, when you hit Ctrl + C while fzf
is running, it returns error code 130
, and the part on the right will never execute.
If you need to do anything more-complicated, however, use a function (in your ~/.bashrc
or ~/.bash_aliases
file still if you like) instead of an alias. Here is a more-robust example of the alias above, in function form. The way I've written the alias above, I expect it to fail if you have filenames with whitespace or strange characters in them. However, the below function should work even in those cases I think:
fz() {
files_list="$(fzf -m)"
return_code="$?"
if [ "$return_code" -ne 0 ]; then
echo "Nothing to do; fzf return_code = $return_code"
return "$return_code"
fi
echo "FILES SELECTED:"
echo "$files_list"
# Convert the above list of newline-separated file names into an array
# - See: https://stackoverflow.com/a/24628676/4561887
SAVEIFS=$IFS # Save current IFS (Internal Field Separator)
IFS=$'\n' # Change IFS to newline char
files_array=($files_list) # split this newline-separated string into an array
IFS=$SAVEIFS # Restore original IFS
# Call vim with each member of the array as an argument
vim -p "${files_array[@]}"
}
Note: if you're not familiar with the [
function (yes, that's a function name!), run man [
or man test
for more information. The [
function is also called test
. That's why the arguments after that function name require spaces between them--they are arguments to a function! The ]
symbol is simply the required closing argument to the [
(test
) function.
return
to exit a bash function early: How to exit a function in bashUpvotes: 2