4

I often find that when using the command line, I need to copy a path that appears in the output of the last command. For example I might use find . | grep like this:

[user@localhost /tmp]$ find . | grep B
./directoryAB
./directoryBA
./directoryBB
./directoryBC
./directoryCB

At this point I need to pick up the mouse, copy the path I want, and type cd, then paste. But it would be rad if I could loop over paths in the output and copy the one I want, just using the keyboard. Is there anything that could help me do this?

I use zsh and iTerm2, fwiw.

6
  • Perhaps the answer involves running the command again, piping its results into sed -n 1p | pbcopy if I want the first line, or changing that 1 to a 3 if I want the third line, and so on.
    – Raffi
    Dec 12, 2022 at 14:26
  • 1
    A similar question has been addressed before, please see : superuser.com/questions/93573/…
    – Miiyr
    Dec 12, 2022 at 14:30
  • 2
    Not an answer to your question, but maybe to your problem: superuser.com/a/1279568/195224
    – mpy
    Dec 12, 2022 at 17:39
  • @mpy This is awesome.
    – Raffi
    Dec 12, 2022 at 19:06
  • 1
    Not really an answer unless you're open to changing terminal, but wezterm can do this without having to pipe commands through fzf etc ahead of time. It'll also let you pick paths out of long form text, eg a commit message.
    – Holloway
    Dec 13, 2022 at 10:56

3 Answers 3

6

It's easier to achieve this with tools that are directly used together with the program that produces the output, instead of trying to scrape it back from what was already printed.

There are tools that can show an interactive "select item" or "select file" prompt and are designed to feed it back into the shell as a command. fzf and broot are two examples.

fzf can accept any input through stdin and let you interactively filter through it, for example:

cd "$(find -type d | fzf)"

This can be wrapped in a shell function, or used as an enhanced Tab-complete (Bash code for this is included with fzf), or even bound to a custom keyboard shortcut, e.g. Alt+D to select a directory via fzf and insert it at cursor position (my aliases.sh has an example for Bash, though it would need to be done very differently for Zsh).

broot on the other hand is specifically a file tree browser, but is also designed to be used through a shell function wrapper.

broot --print-shell-function >> ~/.bashrc

Running the newly created br function will open a file browser, in which you selecting a folder with Alt-Enter will cause the parent shell to cd to the selected location.

3
  • This is terrific. I can pipe git status and other commands into fzf, and then pipe the output of that into pbcopy, or modify the string so that it can become an argument for other commands like vim.
    – Raffi
    Dec 12, 2022 at 22:30
  • Thank you for the details about broot!
    – Raffi
    Dec 13, 2022 at 13:57
  • man broot says the option is --print-shell-function <bash|fish|zsh>. This worked for me: broot --print-shell-function zsh >> ~/.zshrc.
    – Raffi
    Dec 13, 2022 at 14:03
1

Here's one solution. First, you add these aliases to your .bashrc, .zshrc, or equivalent configuration file:

alias l1='fc -e - | sed -n 1p'
alias l2='fc -e - | sed -n 2p'
alias l3='fc -e - | sed -n 3p'
alias l4='fc -e - | sed -n 4p'
alias l5='fc -e - | sed -n 5p'
alias l6='fc -e - | sed -n 6p'
alias l7='fc -e - | sed -n 7p'
alias l8='fc -e - | sed -n 8p'
alias l9='fc -e - | sed -n 9p'
alias c1='fc -e - | sed -n 1p | tr -d \\n | pbcopy'
alias c2='fc -e - | sed -n 2p | tr -d \\n | pbcopy'
alias c3='fc -e - | sed -n 3p | tr -d \\n | pbcopy'
alias c4='fc -e - | sed -n 4p | tr -d \\n | pbcopy'
alias c5='fc -e - | sed -n 5p | tr -d \\n | pbcopy'
alias c6='fc -e - | sed -n 6p | tr -d \\n | pbcopy'
alias c7='fc -e - | sed -n 7p | tr -d \\n | pbcopy'
alias c8='fc -e - | sed -n 8p | tr -d \\n | pbcopy'
alias c9='fc -e - | sed -n 9p | tr -d \\n | pbcopy'
alias L='fc -e - | nl -b a -w2 -s" "'

Then after you run a command, you can type l1 to just see the first line of the output and c1 to copy it. Type L to see the line numbers. (I'm using aliases because fc -e - doesn't work in a script. I added tr -d \\n so that no newlines are added to the clipboard.)

Note that these aliases re-run the command.

The following solution (mentioned by Miiyr in a comment above) might work better for some people: Select text in iTerm using keyboard


Added later:

You can also use a loop like this:

for i in $(seq 1 500); do
  fc="fc -e - | sed -n ${i}p"
  eval "alias l$i='$fc'"
  eval "alias c$i='$fc | tr -d \\n | pbcopy'"
done
4
  • I tested doin this in a function that could accept arguments and options which would prevent having a large number of aliases and managed to set up a fork bomb. Dec 13, 2022 at 16:46
  • Whoops! I also had trouble finding a more elegant solution. Maybe generating the aliases with a loop would be a bit more concise and easier to scale.
    – Raffi
    Dec 13, 2022 at 17:41
  • The bad behavior occurred when I ran a command with a few lines of output then ran my function (equivalent to one of your l aliases) twice. I haven't tried to do that with an alias to see if it does the same thing. I'm using Bash 5.2. Dec 13, 2022 at 18:11
  • @DennisWilliamson I've now added a loop, which is a bit more elegant.
    – Raffi
    Dec 14, 2022 at 14:06
0

This does the job: https://facebook.github.io/PathPicker/. On a Mac you can do: brew install fpp.

Then for example you can do:

git status
fpp

Use up and down (or j and k) until you find the path you want to edit, then hit enter.

p.s. If you're using tmux with oh-my-tmux (https://github.com/gpakosz/.tmux), prefix F is a shortcut for fpp.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .