I'd like to use FZF to search for files and then have them open in an editor of my choice e.g. Sublime, Atom. I'm not sure how to configure my shell for this, I've tried the below but I can't get it to work.
Can you help?
Thanks!
fe() {
local files
IFS=$'\n' files=($(fzf-tmux --query="$1" --multi --select-1 --exit-0))
[[ -n "$files" ]] && ${EDITOR:-atom} "${files[#]}"
}
Based on your comments, it is possible the only problem comes from this part :
${EDITOR:-atom}
This expands to the content of variable EDITOR if has a non-null value, and to atom if it is null or unset. It is likely you have that variable initialized to something else than atom. Try using simply atom instead, like this:
fe() {
local files
IFS=$'\n' files=($(fzf-tmux --query="$1" --multi --select-1 --exit-0))
[[ -n "$files" ]] && atom "${files[#]}"
}
Of course, you can also keep the function as it already is, but make sure your environment contains something like EDITOR=atom.
I wrote a function that I keep in my .bashrc which you can use to select any files through fzf and have them passed to whatever program you want (so not only sublime, but any GUI program you add to the function's list) and it also works with command line tools like cd, cat, tail, head and so on. Also you can cycle back through your history and find the command as it was expanded after fzf did its thing. If you configure fzf to look in many common places on the file system by default (or see here) this function really shines. I use it many times every day, mainly to change directory (f cd) or open files.
In your case you would just type in the terminal:
f sublime
and fzf would launch, after you select your file(s) sublime would open them.
I put the function below, and I got inspiration for it here
#!/bin/bash
# Run command/application and choose paths/files with fzf.
# Always return control of the terminal to user (e.g. when opening GUIs).
# The full command that was used will appear in your history just like any
# other (N.B. to achieve this I write the shell's active history to
# ~/.bash_history)
#
# Usage:
# f cd [OPTION]... (hit enter, choose path)
# f cat [OPTION]... (hit enter, choose files)
# f vim [OPTION]... (hit enter, choose files)
# f vlc [OPTION]... (hit enter, choose files)
f() {
# if no arguments passed, just lauch fzf
if [ $# -eq 0 ]
then
fzf | sort
return 0
fi
# store the program
program="$1"
# remove first argument off the list
shift
# store any option flags
options="$#"
# store the arguments from fzf
arguments=$(fzf --multi)
# if no arguments passed (e.g. if Esc pressed), return to terminal
if [ -z "${arguments}" ]; then
return 1
fi
# sanitise the command:
# put an extra single quote next to any pre-existing single quotes
# put single quotes around each argument
# put them all on one line.
for arg in "${arguments[#]}"; do
arguments=$(echo "$arg" | sed "s/'/''/g;
s/.*/'&'/g;
s/\n//g"
)
done
# if the program is on the GUI list, add a '&'
if [[ "$program" =~ ^(nautilus|zathura|evince|vlc|eog|kolourpaint)$ ]]; then
arguments="$arguments &"
fi
# write the shell's active history to ~/.bash_history.
history -w
# add the command with the sanitised arguments to .bash_history
echo $program $options $arguments >> ~/.bash_history
# reload the ~/.bash_history into the shell's active history
history -r
# execute the last command in history
fc -s -1
}
For those seeking for a general way to open results in MacOS:
Define this alias to open file by os default app (based on selected file type):
alias f='open "$(fzf)"'
Then type f command, find your file and hit ENTER.
Good day,
I am writing a simple script within my BASHRC file to accommodate something I couldn't quite resolve in a previous question:
Side-by-side view in Vim of svn-diff for entire directory
I basically generate a list of all files which have a "Modified" SVN status. For each of these files, I want to create a side-by-side visual diff, convert it to HTML, then append it to a running HTML file.
eg:
MODIFIED_FILES="$(svn status | grep "^M" | cut -c9-)"
for i in ${MODIFIED_FILES}; do
# Generate a side-by-side diff in vim via VIMDIFF
# Convert via ToHTML
# Append the HTML file to a file called "overall_diff.html"
done
I can accomplish the vimdiff easily enough by creating a clean copy of the file, and having a copy of the modified file.
vimdiff has an issue at first, ie:
2 files to edit
Error detected while processing /Users/Owner/.vimrc:
line 45:
E474: Invalid argument: listchars=tab:>-,trail:.,extends:>,precedes:«
Press ENTER or type command to continue
So, I am trying to get past this so I don't have to hit ENTER for each file in my list.
Next, I need to have vimdiff call the ToHTML command, and then issue the command to append the HTML buffer to a running file:
:'<,'>w! >>overall_diff.html
In short, how do I:
Get past this issue with listchars when vimdiff is called. This issue doesn't occur when I run vim, so I don't know why it occurs when I run vimdiff.
Pass a list of colon-commands to VIM to have it run them at startup without requiring a change to my .vimrc file.
In the end, I created a separate VIMRC file that gets passed to the vim command at run time, via:
`vim -d file1 fil2 -u my_special_vimrc_file`
function createVimDiff()
{
# Create some buffers
TEMP_FILE="./tmp_file"
VIM_TEMP="./temp.html"
REVISION=""
BUFFER_FILE="./overall_diff.html"
# Get a list of the files that have changed
MODIFIED_FILES="$(svn status | grep '^M' | cut -c9-)"
# Remove buffers
rm "${BUFFER_FILE}"
for i in ${MODIFIED_FILES}; do
# Remove intermediate buffers
rm "${TEMP_FILE}"
rm "${VIM_TEMP}"
# Get the current SVN rev number for the current file
REVISION="$(svn info ${i} | grep Revision)"
# Echo the name of the file to the report
echo "FILE: ${i}" >> "${BUFFER_FILE}"
# Same with the revision number
echo "${REVISION}" >> "${BUFFER_FILE}"
echo "<br>" >> "${BUFFER_FILE}"
# First print a copy of the unmodified file in a temporary buffer
svn cat "${i}" > "${TEMP_FILE}"
# Now print the unmodified file on the left column, and the
# modified file in the right column, so they appear side-by-side
vim -d "${TEMP_FILE}" "${i}" -u ~/.vimdiff_rc
# Write the side-by-side diff to a file
cat "${VIM_TEMP}" >> "${BUFFER_FILE}"
echo "<br>" >> "${BUFFER_FILE}"
done
# Cleanup temporary buffers
rm "${TEMP_FILE}"
rm "${VIM_TEMP}"
}
And the following was put into my VIMRC file:
" Convert the diff to HTML
autocmd VimEnter * silent TOhtml
" Write output to temporary buffer
autocmd VimEnter * w! ./temp.html
" Quit VIM
autocmd VimEnter * qa!
For example, let's suppose I just copied something:
mv foo_file.txt ~/to/some/long/path/that/i/do/not/want/to/retype
and I'd like to use history substitution like so:
mv bar_file.txt !!:2
I'm surprised that zsh is not expanding the !!:2 for me when I hit [tab]. In a more complex reference to a historical argument I might really want the expansion before I hit return, just so I know with certainty that I referred to the correct argument. Is there any way to make it do that? (I would expect that to be the default behavior. Is it the default behavior, that I have somehow inadvertently disabled or broken?)
If zsh can't do it, can bash?
UPDATE: zsh will expand the history expression if it refers to a file, but not a directory:
mv foo_file.txt foo_bar_file.txt
mv bar_file.txt !!:2[TAB]
It will expand it if it is just an arbitrary string:
echo one two three four
echo !!:1[TAB]
But not if you're trying to move something to a directory. It looks more and more like this must be a bug.
I am using zsh in cygwin:
$ zsh --version
zsh 4.3.12 (i686-pc-cygwin)
$ setopt
interactive
monitor
shinstdin
zle
I just tried the following:
$ touch foo_file.txt bar_file.txt
$ mkdir -p ~/to/some/long/path/that/i/do/not/want/to/retype
$ mv foo_file.txt ~/to/some/long/path/that/i/do/not/want/to/retype
I then tried the tab completion mentioned above:
$ mv bar_file.txt !!:2[TAB]
and it worked fine, the last argument being expanded as follows:
$ mv bar_file.txt ~/to/some/long/path/that/i/do/not/want/to/retype
You can pseudo-hack it in bash:
$ shopt -s histreedit
$ shopt -s histverify
Then, to actually try an expansion:
$ echo !!:2 [now hit enter]
$ echo histverify
Now you can't do tab expansion in bash. Unequivocally no. That's because of the order in which bash expansion is processed.
Works perfectly for me with zsh 4.3.17. Sounds like you probably have a bug which might be worth reporting on the zsh-user mailing list. However there are at least five other keybindings which should accomplish what you want: C-x * which is by default bound to expand-word, and Esc Space or Meta-Space or Esc ! or Meta-! which are all bound to expand-history by default. (Meta means the Alt key for many people, although it depends on your terminal setup.)
Having said that, Esc . (or Meta-. or Alt-.) is a nicer way of retrieving the last word from the previous line in the history, since it provides instant visual feedback. You can also choose the last word from older lines by repeatedly pressing the keyboard shortcut, or even the n th last word on a previous line by prefixing the shortcut with Alt-n (or Meta-n or Esc n). So for example to retrieve the penultimate word from the 3rd newest line of history, the sequence would be:
Meta-. (goes back one line of history, selecting the last word from that line)
Meta-. (goes back another, again selecting the last word)
Meta-2 Meta-. (goes back another, but this time selects the penultimate word from that line)
I've tried what you've described, and I don't think bash supports this either.
In bash, Alt-. is typically bound to yank-last-arg, which will give you what you want.
Here is a link to the whole list of history related commands that can be bound to keystrokes
http://www.gnu.org/software/bash/manual/bashref.html#Commands-For-History
For example,
ls /etc/passwd /etc/group
cat
# press Alt+. (Alt dot) here, bash adds /etc/group
cat /etc/group
# press space, then press Alt+1 Alt+.
# bash adds the first argument of the previous command /etc/passwd
cat /etc/group /etc/passwd
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
We all know how to use <ctrl>-R to reverse search through history, but did you know you can use <ctrl>-S to forward search if you set stty stop ""? Also, have you ever tried running bind -p to see all of your keyboard shortcuts listed? There are over 455 on Mac OS X by default.
What is your single most favorite obscure trick, keyboard shortcut or shopt configuration using bash?
Renaming/moving files with suffixes quickly:
cp /home/foo/realllylongname.cpp{,-old}
This expands to:
cp /home/foo/realllylongname.cpp /home/foo/realllylongname.cpp-old
cd -
It's the command-line equivalent of the back button (takes you to the previous directory you were in).
Another favorite:
!!
Repeats your last command. Most useful in the form:
sudo !!
My favorite is '^string^string2' which takes the last command, replaces string with string2 and executes it
$ ehco foo bar baz
bash: ehco: command not found
$ ^ehco^echo
foo bar baz
Bash command line history guide
rename
Example:
$ ls
this_has_text_to_find_1.txt
this_has_text_to_find_2.txt
this_has_text_to_find_3.txt
this_has_text_to_find_4.txt
$ rename 's/text_to_find/been_renamed/' *.txt
$ ls
this_has_been_renamed_1.txt
this_has_been_renamed_2.txt
this_has_been_renamed_3.txt
this_has_been_renamed_4.txt
So useful
I'm a fan of the !$, !^ and !* expandos, returning, from the most recent submitted command line: the last item, first non-command item, and all non-command items. To wit (Note that the shell prints out the command first):
$ echo foo bar baz
foo bar baz
$ echo bang-dollar: !$ bang-hat: !^ bang-star: !*
echo bang-dollar: baz bang-hat: foo bang-star: foo bar baz
bang-dollar: baz bang-hat: foo bang-star: foo bar baz
This comes in handy when you, say ls filea fileb, and want to edit one of them: vi !$ or both of them: vimdiff !*. It can also be generalized to "the nth argument" like so:
$ echo foo bar baz
$ echo !:2
echo bar
bar
Finally, with pathnames, you can get at parts of the path by appending :h and :t to any of the above expandos:
$ ls /usr/bin/id
/usr/bin/id
$ echo Head: !$:h Tail: !$:t
echo Head: /usr/bin Tail: id
Head: /usr/bin Tail: id
When running commands, sometimes I'll want to run a command with the previous ones arguments. To do that, you can use this shortcut:
$ mkdir /tmp/new
$ cd !!:*
Occasionally, in lieu of using find, I'll break-out a one-line loop if I need to run a bunch of commands on a list of files.
for file in *.wav; do lame "$file" "$(basename "$file" .wav).mp3" ; done;
Configuring the command-line history options in my .bash_login (or .bashrc) is really useful. The following is a cadre of settings that I use on my Macbook Pro.
Setting the following makes bash erase duplicate commands in your history:
export HISTCONTROL="erasedups:ignoreboth"
I also jack my history size up pretty high too. Why not? It doesn't seem to slow anything down on today's microprocessors.
export HISTFILESIZE=500000
export HISTSIZE=100000
Another thing that I do is ignore some commands from my history. No need to remember the exit command.
export HISTIGNORE="&:[ ]*:exit"
You definitely want to set histappend. Otherwise, bash overwrites your history when you exit.
shopt -s histappend
Another option that I use is cmdhist. This lets you save multi-line commands to the history as one command.
shopt -s cmdhist
Finally, on Mac OS X (if you're not using vi mode), you'll want to reset <CTRL>-S from being scroll stop. This prevents bash from being able to interpret it as forward search.
stty stop ""
How to list only subdirectories in the current one ?
ls -d */
It's a simple trick, but you wouldn't know how much time I needed to find that one !
ESC.
Inserts the last arguments from your last bash command. It comes in handy more than you think.
cp file /to/some/long/path
cd ESC.
Sure, you can "diff file1.txt file2.txt", but Bash supports process substitution, which allows you to diff the output of commands.
For example, let's say I want to make sure my script gives me the output I expect. I can just wrap my script in <( ) and feed it to diff to get a quick and dirty unit test:
$ cat myscript.sh
#!/bin/sh
echo -e "one\nthree"
$
$ ./myscript.sh
one
three
$
$ cat expected_output.txt
one
two
three
$
$ diff <(./myscript.sh) expected_output.txt
1a2
> two
$
As another example, let's say I want to check if two servers have the same list of RPMs installed. Rather than sshing to each server, writing each list of RPMs to separate files, and doing a diff on those files, I can just do the diff from my workstation:
$ diff <(ssh server1 'rpm -qa | sort') <(ssh server2 'rpm -qa | sort')
241c240
< kernel-2.6.18-92.1.6.el5
---
> kernel-2.6.18-92.el5
317d315
< libsmi-0.4.5-2.el5
727,728d724
< wireshark-0.99.7-1.el5
< wireshark-gnome-0.99.7-1.el5
$
There are more examples in the
Advanced Bash-Scripting Guide at http://tldp.org/LDP/abs/html/process-sub.html.
My favorite command is "ls -thor"
It summons the power of the gods to list the most recently modified files in a conveniently readable format.
More of a novelty, but it's clever...
Top 10 commands used:
$ history | awk '{print $2}' | awk 'BEGIN {FS="|"}{print $1}' | sort | uniq -c | sort -nr | head
Sample output:
242 git
83 rake
43 cd
33 ss
24 ls
15 rsg
11 cap
10 dig
9 ping
3 vi
^R reverse search. Hit ^R, type a fragment of a previous command you want to match, and hit ^R until you find the one you want. Then I don't have to remember recently used commands that are still in my history. Not exclusively bash, but also: ^E for end of line, ^A for beginning of line, ^U and ^K to delete before and after the cursor, respectively.
I often have aliases for vi, ls, etc. but sometimes you want to escape the alias. Just add a back slash to the command in front:
Eg:
$ alias vi=vim
$ # To escape the alias for vi:
$ \vi # This doesn't open VIM
Cool, isn't it?
Here's a couple of configuration tweaks:
~/.inputrc:
"\C-[[A": history-search-backward
"\C-[[B": history-search-forward
This works the same as ^R but using the arrow keys instead. This means I can type (e.g.) cd /media/ then hit up-arrow to go to the last thing I cd'd to inside the /media/ folder.
(I use Gnome Terminal, you may need to change the escape codes for other terminal emulators.)
Bash completion is also incredibly useful, but it's a far more subtle addition. In ~/.bashrc:
if [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
This will enable per-program tab-completion (e.g. attempting tab completion when the command line starts with evince will only show files that evince can open, and it will also tab-complete command line options).
Works nicely with this also in ~/.inputrc:
set completion-ignore-case on
set show-all-if-ambiguous on
set show-all-if-unmodified on
I use the following a lot:
The :p modifier to print a history result. E.g.
!!:p
Will print the last command so you can check that it's correct before running it again. Just enter !! to execute it.
In a similar vein:
!?foo?:p
Will search your history for the most recent command that contained the string 'foo' and print it.
If you don't need to print,
!?foo
does the search and executes it straight away.
I have got a secret weapon : shell-fu.
There are thousand of smart tips, cool tricks and efficient recipes that most of the time fit on a single line.
One that I love (but I cheat a bit since I use the fact that Python is installed on most Unix system now) :
alias webshare='python -m SimpleHTTPServer'
Now everytime you type "webshare", the current directory will be available through the port 8000. Really nice when you want to share files with friends on a local network without usb key or remote dir. Streaming video and music will work too.
And of course the classic fork bomb that is completely useless but still a lot of fun :
$ :(){ :|:& };:
Don't try that in a production server...
You can use the watch command in conjunction with another command to look for changes. An example of this was when I was testing my router, and I wanted to get up-to-date numbers on stuff like signal-to-noise ratio, etc.
watch --interval=10 lynx -dump http://dslrouter/stats.html
type -a PROG
in order to find all the places where PROG is available, usually somewhere in ~/bin
rather than the one in /usr/bin/PROG that might have been expected.
I like to construct commands with echo and pipe them to the shell:
$ find dir -name \*~ | xargs echo rm
...
$ find dir -name \*~ | xargs echo rm | ksh -s
Why? Because it allows me to look at what's going to be done before I do it. That way if I have a horrible error (like removing my home directory), I can catch it before it happens. Obviously, this is most important for destructive or irrevocable actions.
When downloading a large file I quite often do:
while ls -la <filename>; do sleep 5; done
And then just ctrl+c when I'm done (or if ls returns non-zero). It's similar to the watch program but it uses the shell instead, so it works on platforms without watch.
Another useful tool is netcat, or nc. If you do:
nc -l -p 9100 > printjob.prn
Then you can set up a printer on another computer but instead use the IP address of the computer running netcat. When the print job is sent, it is received by the computer running netcat and dumped into printjob.prn.
pushd and popd almost always come in handy
One preferred way of navigating when I'm using multiple directories in widely separate places in a tree hierarchy is to use acf_func.sh (listed below). Once defined, you can do
cd --
to see a list of recent directories, with a numerical menu
cd -2
to go to the second-most recent directory.
Very easy to use, very handy.
Here's the code:
# do ". acd_func.sh"
# acd_func 1.0.5, 10-nov-2004
# petar marinov, http:/geocities.com/h2428, this is public domain
cd_func ()
{
local x2 the_new_dir adir index
local -i cnt
if [[ $1 == "--" ]]; then
dirs -v
return 0
fi
the_new_dir=$1
[[ -z $1 ]] && the_new_dir=$HOME
if [[ ${the_new_dir:0:1} == '-' ]]; then
#
# Extract dir N from dirs
index=${the_new_dir:1}
[[ -z $index ]] && index=1
adir=$(dirs +$index)
[[ -z $adir ]] && return 1
the_new_dir=$adir
fi
#
# '~' has to be substituted by ${HOME}
[[ ${the_new_dir:0:1} == '~' ]] && the_new_dir="${HOME}${the_new_dir:1}"
#
# Now change to the new dir and add to the top of the stack
pushd "${the_new_dir}" > /dev/null
[[ $? -ne 0 ]] && return 1
the_new_dir=$(pwd)
#
# Trim down everything beyond 11th entry
popd -n +11 2>/dev/null 1>/dev/null
#
# Remove any other occurence of this dir, skipping the top of the stack
for ((cnt=1; cnt <= 10; cnt++)); do
x2=$(dirs +${cnt} 2>/dev/null)
[[ $? -ne 0 ]] && return 0
[[ ${x2:0:1} == '~' ]] && x2="${HOME}${x2:1}"
if [[ "${x2}" == "${the_new_dir}" ]]; then
popd -n +$cnt 2>/dev/null 1>/dev/null
cnt=cnt-1
fi
done
return 0
}
alias cd=cd_func
if [[ $BASH_VERSION > "2.05a" ]]; then
# ctrl+w shows the menu
bind -x "\"\C-w\":cd_func -- ;"
fi
Expand complicated lines before hitting the dreaded enter
Alt+Ctrl+e — shell-expand-line (may need to use Esc, Ctrl+e on your keyboard)
Ctrl+_ — undo
Ctrl+x, * — glob-expand-word
$ echo !$ !-2^ * Alt+Ctrl+e
$ echo aword someotherword * Ctrl+_
$ echo !$ !-2^ * Ctrl+x, *
$ echo !$ !-2^ LOG Makefile bar.c foo.h
&c.
I've always been partial to:
ctrl-E # move cursor to end of line
ctrl-A # move cursor to beginning of line
I also use shopt -s cdable_vars, then you can create bash variables to common directories. So, for my company's source tree, I create a bunch of variables like:
export Dcentmain="/var/localdata/p4ws/centaur/main/apps/core"
then I can change to that directory by cd Dcentmain.
pbcopy
This copies to the Mac system clipboard. You can pipe commands to it...try:
pwd | pbcopy
$ touch {1,2}.txt
$ ls [12].txt
1.txt 2.txt
$ rm !:1
rm [12].txt
$ history | tail -10
...
10007 touch {1,2}.txt
...
$ !10007
touch {1,2}.txt
$ for f in *.txt; do mv $f ${f/txt/doc}; done
Using 'set -o vi' from the command line, or better, in .bashrc, puts you in vi editing mode on the command line. You start in 'insert' mode so you can type and backspace as normal, but if you make a 'large' mistake you can hit the esc key and then use 'b' and 'f' to move around as you do in vi. cw to change a word. Particularly useful after you've brought up a history command that you want to change.
Similar to many above, my current favorite is the keystroke [alt]. (Alt and "." keys together) this is the same as $! (Inserts the last argument from the previous command) except that it's immediate and for me easier to type. (Just can't be used in scripts)
eg:
mkdir -p /tmp/test/blah/oops/something
cd [alt].
String multiple commands together using the && command:
./run.sh && tail -f log.txt
or
kill -9 1111 && ./start.sh