Como reexecutar o comando k'th em um pipeline?

3

Eu tenho o seguinte pipeline:

➜  echo ,cats,and,dogs, | sed -e 's/,[^,]*,[^,]*,/,,,/'
,,,dogs,

Eu sei que eu poderia executar um comando como !! para "executar o último comando" ou !:1 para "obter os últimos argumentos", mas eu estou querendo saber se há algum comando que eu possa executar que me permitirá "obter o comando k th + args de um pipeline"

Portanto, neste exemplo, se eu quisesse enviar alguma outra saída para o utilitário sed , poderia fazer algo assim logo após executar o pipeline acima:

$ echo ,foo,bar,baz, | %:2

onde %:2 é algum comando talvez-fictício que eu não sei, que "executa o comando k th em um pipeline" Este comando existe?

    
por mbigras 12.10.2016 / 08:46

2 respostas

3

A expansão da história foi grande nos anos 80 (quando introduzida por csh ) quando os terminais eram muito lentos e limitados. O problema é que eles não são WYSIWYG. Você não vê o que ele expande até que seja tarde demais (o comando já foi iniciado) (embora alguns shell permitam executar a expansão antes de pressionar Enter).

Aqui, eu criaria um widget que insira o último elemento de pipeline no cursor. Com zsh (adicione a ~/.zshrc ):

last-pipe-elements() {
  emulate -L zsh
  if [[ $WIDGET = $LASTWIDGET ]]; then
    # Invoking the widget several times retrieves pipeline
    # elements for older command lines
    ((last_pipe_elements_iteration++))
    LBUFFER=$last_pipe_elements_saved_LBUFFER
  else
    last_pipe_elements_iteration=1
  fi
  last_pipe_elements_saved_LBUFFER=$LBUFFER

  local -a words
  words=(${(z)${history[@]}[last_pipe_elements_iteration]})

  local nth_pipe=${words[(In:NUMERIC:)\|]}
  ((nth_pipe)) || return

  LBUFFER+=${(j: :)words[nth_pipe,-1]}
}
zle -N last-pipe-elements
bindkey '\eP' last-pipe-elements

Dessa forma, você digita Alt + Shift + P (presume-se que envie a seqüência de caracteres ESC e P , pode ser necessário adaptá-lo para o seu terminal que pode enviar \xd0 ( $'\MP' ) ou mesmo \xf0 ($ '\ Mp') ou algo completamente diferente, verifique com Ctrl + V ) para inserir o último elemento do pipeline (incluindo | ) do último comando, pressione-o novamente para o comando antes disso, e use Alt + 2 Alt + Shift + P para os últimos 2 elementos do pipe.

(z) chama o analisador de shell para dividir as linhas de comando em palavras. Encontramos o | que queremos e depois juntamos as palavras à direita do espaço one . Isso significa que |tr a b se tornará | tr a b , mas isso não deve afetar o significado do comando, desde que você não use construções complexas de várias linhas ( sed 's/ /x/' não será alterado para sed 's/ /x/' , por exemplo ).

Se você não quiser definir um novo widget, outra coisa que você pode fazer é pesquisar de volta (com Ctrl + R assumindo o modo emacs , embora você possa fazer o equivalente em vi mode) para o | anterior, apague para o final da linha com Ctrl + K e volte ao seu comando ( Down ) para cole-o com Ctrl + Y . Isso ainda seria mais rápido do que usar a expansão do histórico e ainda seria visual (você pode selecionar visualmente qual | você quer cortar).

    
por 12.10.2016 / 11:00
3

Não sabe como conseguir "depois do cano", mas "depois do terceiro espaço em branco" pode ajudar:

$ echo ,cats,and,dogs, | sed -e 's/,[^,]*,[^,]*,/,,,/'
,,,dogs,
$ echo ,cat,and,mouse, | !:3-$
,,,mouse,

Solução alternativa para "after pipe":

$ echo ,cats,and,dogs, | sed -e 's/,[^,]*,[^,]*,/,,,/'
,,,dogs,
$ after_pipe="$(cut -d "|" -f 2- <<<"!!" )"
$ echo ,cat,and,mouse, | eval "$after_pipe"
$ ,,,mouse,

( cut precisaria ser algo melhor se houver pipes "|" no primeiro comando).

    
por 12.10.2016 / 09:08