Bash: captura / usa la última (o enésima línea) en stdout

11

Consulta

Yo uso Bash. Cuando busco archivos, a menudo hago lo siguiente:

find -name stackexchange.hs

Y a menudo los resultados se verán así:

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

Entonces querré hacer uno de los siguientes:

  • Opción 1: abra el último elemento de la lista de resultados en vim .
  • Opción 2: abra el enésimo elemento en la lista de resultados en vim .

Actualmente, corto y pego con el mouse. Lo que me lleva a mi pregunta :

  1. ¿Hay una línea fácil y simple para lograr las opciones 1 y 2? Tenga en cuenta que esto ocurre después del findcomando.
  2. ¿Hay alguna manera de capturar líneas N de stdout en algún tipo de vector / matriz 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)

(la sintaxis y la semántica pueden diferir, pero entiendes el punto)

Similaria

Parece que hay varias preguntas similares. Aquí están mis diferencias percibidas (estoy abierto a la iluminación):

¡Gracias por tu ayuda! Después de haber usado * nix / BSD cuando era un adolescente en los años 90 y me asusté llamando a mi vecino agotado y ácido para ayudarme a instalar controladores para mi tarjeta de sonido plug-and-play, me siento aliviado de hablar sobre el comando alinear minucias con individuos (perceptiblemente) menos aterradores. Se siente bien estar de vuelta.

aaronlevin
fuente
Creo que si lo sabes antes que quieres abrir el último resultado, podrías usar algo como vim $(command |tail -n1).
varesa
Publiqué
joelostblom

Respuestas:

8

Aquí hay una posible solución a su problema que debería ser razonablemente segura (pero no perfectamente) en presencia de nombres de archivos originales (no maneja nombres de archivos con saltos de línea), probablemente reparable, pero puede haber otros problemas al acecho).

Dos funciones, la primera se ejecuta findcon los parámetros que le pasa, guarda la salida en una matriz y las muestra. El segundo es solo un ayudante para acceder a esa matriz.

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"

No se requieren comillas $(myget index)si no tiene espacios en blanco u otros caracteres problemáticos en sus nombres de archivo.
Empuja toda la salida finda su entorno, que puede ser limitado. (El uso de un archivo temporal en lugar de esa matriz resolvería eso, pero tiene otros problemas, especialmente el uso concurrente de múltiples shells).

Estera
fuente
1
No puedo votarlo porque no tengo suficiente reputación, así que aquí hay una, uh, verbal: "
voto positivo
6

Tengo esto en mi .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

Básicamente, en la pantalla, ¬1pega la línea sobre el cursor ¬2, pega la segunda línea sobre el cursor ... y así sucesivamente. Es posible que desee agregar más para las líneas 10 y superiores, pero creo que después de aproximadamente las 7, prefiero usar el mouse o screenel modo de copia que contar el número de líneas para obtener la que quiero.

Stéphane Chazelas
fuente
0

Otra solución es: puede escribir una secuencia de comandos interactiva que le pedirá automáticamente su elección. Aquí está el código para el script interactivo:

#!/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

guarde este script con cualquier nombre, diga "autofind" e invoque el script con su "comando find" como argumento aquí está el código para invocar el script:

./autofind `your find command`

Pero antes de usar el script, verifique su "comando find" si está dando resultado o no. Si está mostrando algún resultado, entonces solo use el script

usuario1678213
fuente
0

La respuesta de Mats fue justo lo que estaba buscando. He ampliado un poco su código para permitir más opciones de obtención.

$ 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
}
bumby
fuente