Bash: cattura / usa l'ultima (o l'ennesima) riga nello stdout


11

domanda

Io uso Bash. Quando cerco file, spesso faccio quanto segue:

find -name stackexchange.hs

E spesso i risultati saranno simili:

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

Quindi voglio fare una delle seguenti operazioni:

  • Opzione 1: aprire l'ultimo elemento nell'elenco dei risultati in vim .
  • Opzione 2: Apri l'ennesimo elemento nell'elenco dei risultati in vim .

Attualmente, taglio e incolla con il mouse. Il che mi porta alla mia domanda :

  1. Esiste un programma semplice da utilizzare per realizzare le opzioni 1 e 2? Si noti che ciò si verifica dopo il findcomando.
  2. C'è un modo per catturare le linee N dallo stdout in una sorta di vettore / array bash?

Utilizzo ideale

$ 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 sintassi e la semantica possono differire, ma ottieni il punto)

similaria

Sembra che ci siano diverse domande simili. Ecco le mie differenze percepite (sono aperto all'illuminazione):

Grazie per l'aiuto! Avendo usato * nix / BSD quando ero un adolescente negli anni '90 e mi sono spaventato chiamando il mio burnout, vicino di casa acido per aiutarmi a installare i driver per la mia scheda audio plug-and-play, sono sollevato di discutere del comando- minutiae di linea con (percepibilmente) individui meno spaventosi. È bello tornare.


Penso che se lo sai prima che vuoi aprire l'ultimo risultato, potresti usare qualcosa del genere vim $(command |tail -n1).
varesa,

Ho pubblicato una domanda simile qui unix.stackexchange.com/questions/348224/…
joelostblom

Risposte:


8

Ecco una potenziale soluzione al tuo problema che dovrebbe essere ragionevolmente (ma non perfettamente) sicura in presenza di nomi di file funky (non gestisce nomi di file con linefeed in essi - probabilmente risolvibili, ma potrebbero esserci altri problemi in agguato).

Due funzioni, il primo eseguito findcon i parametri si passa, salva l'output in un array, e li visualizza. Il secondo è solo un aiuto per accedere a quell'array.

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

Caso d'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"

Le virgolette non sono necessarie $(myget index)se non hai spazi bianchi o altri caratteri problematici nei nomi dei file.
Invia l'intero output findal tuo ambiente, il che potrebbe essere limitato. (L'uso di un file temporaneo anziché quello dell'array lo risolverebbe, ma presenta altri problemi, in particolare l'uso simultaneo da più shell.)


1
Non posso votarti perché non ho abbastanza reputazione, quindi eccone uno verbale: "voto"
aaronlevin,

6

Ho questo nel mio .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

Fondamentalmente, sullo schermo, ¬1incolla la linea sopra il cursore ¬2, incolla la seconda linea sopra il cursore ... e così via. Potresti voler aggiungere altro per le righe 10 e successive, ma trovo che dopo circa 7, preferirei usare il mouse o screenla modalità di copia piuttosto che contare il numero di righe per ottenere quello che voglio.


0

un'altra soluzione è: puoi scrivere uno script interattivo che chiederà automaticamente la tua scelta. ecco il codice per lo script interattivo:

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

salva questo script con qualsiasi nome, pronuncia "autofind" e invoca lo script con il tuo "comando find" come argomento, ecco il codice per invocare lo script:

./autofind `your find command`

Ma prima di utilizzare lo script controlla il tuo "comando di ricerca" se sta dando un risultato o meno. Se sta mostrando qualche risultato, usa solo lo script


0

La risposta di Mats era proprio quello che stavo cercando. Ho ampliato un po 'il suo codice per consentire ulteriori opzioni 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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.