Bash: capturer / utiliser la dernière (ou la neuvième) ligne dans stdout

11

Requete

J'utilise Bash. Lorsque je recherche des fichiers, je fais souvent ce qui suit:

find -name stackexchange.hs

Et souvent, les résultats ressemblent à:

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

Ensuite, je veux effectuer l'une des opérations suivantes:

  • Option 1: Ouvrez le dernier élément de la liste des résultats dans vim .
  • Option 2: Ouvrez le Nième élément dans la liste des résultats dans vim .

Actuellement, je coupe et colle avec la souris. Ce qui m'amène à ma question :

  1. Existe-t-il une ligne simple pour réaliser les options 1 et 2? Notez que cela se produit après la findcommande.
  2. Existe-t-il un moyen de capturer des lignes N à partir de stdout dans une sorte de vecteur / tableau bash?

Utilisation idéale

$ 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 syntaxe et la sémantique peuvent différer, mais vous obtenez le point)

Similaria

Il semble y avoir plusieurs questions similaires. Voici mes différences perçues (je suis ouvert à l'illumination):

Merci de votre aide! Ayant utilisé * nix / BSD quand j'étais adolescent dans les années 90 et me faisant peur en appelant mon burnout, un voisin acid-head pour m'aider à installer les pilotes de ma carte son plug-and-play, je suis soulagé de discuter de la commande- minutie des lignes avec (perceptiblement) des individus moins effrayants. C'est bon d'être de retour.

aaronlevin
la source
Je pense que si vous le savez avant que vous souhaitez ouvrir le dernier résultat, vous pouvez utiliser quelque chose comme vim $(command |tail -n1).
varesa
J'ai posté une question similaire ici unix.stackexchange.com/questions/348224/…
joelostblom

Réponses:

8

Voici une solution potentielle à votre problème qui devrait être raisonnablement (mais pas parfaitement) sûre en présence de noms de fichiers géniaux (ne gère pas les noms de fichiers avec des sauts de ligne - probablement réparable, mais il pourrait y avoir d'autres problèmes cachés).

Deux fonctions, la première s'exécute findavec les paramètres que vous lui transmettez, enregistre la sortie dans un tableau et les affiche. Le second est juste une aide pour accéder à ce tableau.

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

Cas d'utilisation:

$ 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"

Les citations ne sont pas nécessaires autour du $(myget index)si vous n'avez pas d'espaces blancs ou d'autres caractères gênants dans vos noms de fichiers.
Envoie toute la sortie de finddans votre environnement, ce qui peut être limité. (L'utilisation d'un fichier temporaire plutôt que de ce tableau résoudrait cela, mais a d'autres problèmes - notamment l'utilisation simultanée de plusieurs shells.)

Tapis
la source
1
Je ne peux pas vous voter parce que je n'ai pas assez de réputation, alors voici une, euh, verbale: "upvote"
aaronlevin
6

J'ai ceci dans mon .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

Fondamentalement, à l'écran, ¬1colle la ligne au-dessus du curseur ¬2, colle la deuxième ligne au-dessus du curseur ... et ainsi de suite. Vous voudrez peut-être ajouter plus pour les lignes 10 et supérieures, mais je trouve qu'après environ 7 déjà, je préfère utiliser la souris ou screenle mode de copie plutôt que de compter le nombre de lignes pour obtenir celle que je veux.

Stéphane Chazelas
la source
0

une autre solution est: vous pouvez écrire un script interactif qui vous demandera automatiquement votre choix. voici le code du script interactif:

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

enregistrez ce script avec n'importe quel nom, dites "autofind" et invoquez le script avec votre "commande find" comme argument voici le code pour appeler le script:

./autofind `your find command`

Mais avant d'utiliser le script, vérifiez votre "commande find" si elle donne un résultat ou non. Si elle affiche un résultat, utilisez uniquement le script

user1678213
la source
0

La réponse de Mats était exactement ce que je cherchais. J'ai un peu développé son code pour permettre plus d'options 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
}
bumby
la source