Bash: Capture / Use a última (ou a última) linha no stdout

11

Consulta

Eu uso o Bash. Quando procuro por arquivos, geralmente faço o seguinte:

find -name stackexchange.hs

E muitas vezes os resultados serão parecidos com:

/youre/the/man/now/dog/stackexchange.hs
/you/are/no/longer/the/dog/dog/stackexchange.hs
/this/is/the/file/i/want/stackexchange.hs

Então, eu quero seguir um destes procedimentos:

  • Opção 1: abra o último item na lista de resultados em vim .
  • Opção 2: abra o item enésimo na lista de resultados em vim .

Atualmente, recorto e colo com o mouse. O que me leva à minha pergunta :

  1. Existe um one-liner fácil para realizar as opções 1 e 2? Observe que isso está ocorrendo após o comando find .
  2. Existe uma maneira de capturar N-linhas de stdout em algum tipo de array / vetor bash?

Uso ideal

$ find -name am_i_really_all_alone.txt
./borges/library/you_are_not_alone.txt
./borges/library/am_i_really_all_alone.txt
$ vim (N)

(sintaxe e semântica podem diferir, mas você entendeu)

Similaria

Parece haver várias perguntas semelhantes. Aqui estão as minhas diferenças percebidas (estou aberto à iluminação):

Obrigado pela sua ajuda! Tendo usado o * nix / BSD quando eu era adolescente nos anos 90 e me afugentando chamando meu burnout, vizinho de cabeça ácida para me ajudar a instalar drivers para minha placa de som plug-and-play, estou aliviada em discutir linha minúcias com (perceptivelmente) indivíduos menos assustadores. É bom estar de volta.

    
por aaronlevin 28.12.2012 / 18:20

4 respostas

7

Aqui está uma possível solução para o seu problema que deve ser razoavelmente (mas não perfeitamente) segura na presença de nomes de arquivos (não manipula nomes de arquivos com alimentações de linha neles - provavelmente corrigíveis, mas pode haver outros problemas à espreita).

Duas funções, a primeira executa find com os parâmetros que você passa, salva a saída em uma matriz e as exibe. O segundo é apenas um ajudante para acessar esse array.

myfind() {
  IFS=$'\n' __last_find_result=($(find "$@"));
  printf "%s\n" "${__last_find_result[@]}";
}
myget() {
  echo "${__last_find_result[$1]}";
}

Caso de uso:

$ myfind . -name "c*"
./a b/c d
./.git/config
./.git/hooks/commit-msg.sample
$ vim "$(myget 0)"
# This opens the "./a b/c d" file.
$ vim "$(myget 2)"
# This opens ".git/hooks/commit-msg.sample"

As citações não são necessárias em torno do $(myget index) se você não tiver espaço em branco ou outros caracteres problemáticos em seus nomes de arquivos.
Envia toda a saída de find para o seu ambiente, o que pode ser limitado. (Usar um arquivo temporário em vez daquele array resolveria isso, mas tem outros problemas - principalmente o uso simultâneo de vários shells).

    
por 28.12.2012 / 18:48
6

Eu tenho isso no meu .screenrc :

bind -c pasteline 1 eval copy 'stuff "-Y"' 'paste .'
bind -c pasteline 2 eval copy 'stuff "2-Y"' 'paste .'
bind -c pasteline 3 eval copy 'stuff "3-Y"' 'paste .'
bind -c pasteline 4 eval copy 'stuff "4-Y"' 'paste .'
bind -c pasteline 5 eval copy 'stuff "5-Y"' 'paste .'
bind -c pasteline 6 eval copy 'stuff "6-Y"' 'paste .'
bind -c pasteline 7 eval copy 'stuff "7-Y"' 'paste .'
bind -c pasteline 8 eval copy 'stuff "8-Y"' 'paste .'
bind -c pasteline 9 eval copy 'stuff "9-Y"' 'paste .'
bindkey ¬ command -c pasteline

Basicamente, na tela, ¬ 1 , cola a linha acima do cursor, ¬ 2 , pastas a segunda linha acima do cursor ... e assim por diante. Você pode querer adicionar mais para linhas 10 e acima, mas eu acho que depois de cerca de 7, eu prefiro usar o mouse ou o modo de cópia screen do que contar o número de linhas para obter o que eu quero. / p>     

por 28.12.2012 / 21:19
0

outra solução é: você pode escrever um script interativo que pedirá automaticamente a sua escolha. aqui está o código do script interativo:

#!/bin/bash

echo "enter your choice : z for last argument or a number for that file"
read choice

case "$choice" in
z) eval vim \$$#;;
*)eval  vim \$$choice;;
esac

salve este script com qualquer nome, diga "autofind" e invoque o script com seu "comando find" como argumento aqui está o código para invocar o script:

./autofind 'your find command'

Mas antes de usar o script, verifique seu "comando find" se está dando resultado ou não. Se ele estiver mostrando algum resultado, use somente o script

    
por 30.12.2012 / 07:33
0

A resposta do Mats foi exatamente o que eu estava procurando. Eu expandi seu código um pouco para permitir mais opções get.

$ f ~/scripts -name '*.sh'
$ vim $(g foo)  # edit all find results matching "foo"
$ vim $(g 1 3 5) # edit find results number 1, 3 and 5
$ vim $(g 3-5) # edit find results 3-5
$ vim $(g 5-) # edit find results 5 to last
$ vim $(g -7) # edit find result 7 from bottom
$ vim $(g 1 4-5 -7 9- foo) # all of the above combined

.

f() {
    IFS=$'\n' __last_find_result=($(find "$@"));
    printf "%s\n" "${__last_find_result[@]}";
}

g() {
    len=${#__last_find_result[@]}
    pad=${#len}
    numbers=""
    if [ "$1" == "-n" ]; then
        numbers=1
        shift
    fi
    if [ -z "$1" ]; then
        if [ -n "$numbers" ]; then
            n=1;
            for e in "${__last_find_result[@]}";do
                printf "%0${pad}d. %s\n" "$n" "$e"
                let n=n+1
            done
        else
            printf "%s\n" "${__last_find_result[@]}"
        fi
    else
        for l in $@;do
            if [[ "$l" =~ ([^0-9-]+) ]];then
                n=1;
                for e in "${__last_find_result[@]}";do
                    if [[ $e =~ $1 ]]; then
                        if [ -n "$numbers" ];then
                            printf "%0${pad}d. %s\n" "$n" "$e"
                        else
                            printf "%s\n" "$e"
                        fi
                    fi
                    let n=n+1
                done
            elif [[ "$l" =~ ^([0-9]+)$ ]];then
                let l=l-1
                echo "${__last_find_result[$l]}";
            elif [[ "$l" =~ ^([0-9]*)(-)?([0-9]*)$ ]]; then
                from=${BASH_REMATCH[1]};
                dash=${BASH_REMATCH[2]};
                to=${BASH_REMATCH[3]};
                if [ -z "$from" ]; then # -n
                    [ $to -gt ${#__last_find_result[@]} ] && to=${#__last_find_result[@]}
                    echo "${__last_find_result[-$to]}";
                else # n-m
                    [ -z "$to" ] && to=${#__last_find_result[@]}
                    [ $to -gt ${#__last_find_result[@]} ] && to=${#__last_find_result[@]}
                    let to=$to-1
                    let from=$from-1
                    n=$(($from+1))
                    for i in 'seq $from $to';do
                        if [ -n "$numbers" ];then
                            printf "%0${pad}d. %s\n" "$n" "${__last_find_result[$i]}"
                        else
                            printf "%s\n" "${__last_find_result[$i]}"
                        fi
                        let n=n+1
                    done
                fi
            fi
        done
    fi
}
    
por 18.09.2015 / 12:02