2

I'm trying to run a line from history by piping it into zsh with:

cat ~/.zsh_history | cut -d ';' -f 2 | sk --tac | zsh

This works if the line I pick interactively with sk is a command that is available in the PATH, like man zsh.

But if it is something with an alias, like ck, instead of running the expansion cd .. && ls, I get a "command not found" error.


Edit: also, if I type:

echo "cd .. && ls" | zsh

It prints the output of running ls in the parent directory, but when the command exits, I get back to the original prompt in the child directory. Is there a way to make zsh treat the thing piped into it as if I had typed it manually in the prompt and hit enter?

| improve this question | | | | |
  • No luck :/ ``` $ echo "ck" | zsh --rcs zsh: command not found: ck ``` – agentofuser Oct 2 '19 at 16:04
1

Piping code into zsh runs that code in a separate process. This process doesn't have your interactive settings such as aliases and functions (it doesn't read .zshrc) and doesn't have the shell variables and other state of the current shell. The state of that other process, such as the current directory, is lost when that process exits.

To execute code in the current shell process, pass it as an argument (not as input) to the eval builtin.

eval "$(<~/.zsh_history cut -d ';' -f 2 | sk --tac)"

But for this specific use case of selecting a history entry, there's an other way. The fc builtin. The following command runs the function fcedit_sk with one argument, which is the name of a temporary file. When the function is called, the file contains all the history entries (${${(kn)history}[1]} is the index of the oldest entry and -1 means the newest entry). When the function returns, zsh executes whatever the file contains as if you had entered it at the prompt.

fc -e fcedit_sk ${${(kn)history}[1]} -1

Make fcedit_sk a function that calls sk on the content of the file and writes its output back to the file. I don't know sk, so I don't know if there's an easier way than this:

fcedit_sk () {
  local output
  output=$(sk --tac <$1)
  if (($?)); then
    echo >|$1 # Cancelled
  else
    print -lr -- $output >|$1
  fi
}
| improve this answer | | | | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.