Bash: Capture / Use last (or Nth) line in stdout

11

Pytanie

Używam Bash. Kiedy szukam plików, często wykonuję następujące czynności:

find -name stackexchange.hs

I często wyniki będą wyglądać następująco:

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

Następnie chcę wykonać jedną z następujących czynności:

  • Opcja 1: Otwórz ostatni element na liście wyników w vimie .
  • Opcja 2: Otwórz n-ty element na liście wyników w vimie .

Obecnie wycinam i wklejam za pomocą myszy. Co prowadzi mnie do mojego pytania :

  1. Czy istnieje łatwy, jednoliniowy sposób na wykonanie opcji 1 i 2? Zauważ, że to dzieje się po tym findpoleceniu.
  2. Czy istnieje sposób na uchwycenie linii N ze standardowego wejścia w jakimś wektorze / tablicy bash?

Idealne wykorzystanie

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

(składnia i semantyka mogą się różnić, ale masz rację)

Likeia

Wydaje się, że istnieje kilka podobnych pytań. Oto moje postrzegane różnice (jestem otwarty na oświecenie):

Dziękuję za pomoc! Korzystając z * nix / BSD, kiedy byłem nastolatkiem w latach 90. i przestraszyłem się, dzwoniąc do mojego wypalonego sąsiada, który pomógł mi zainstalować sterowniki do mojej karty dźwiękowej plug-and-play, z ulgą omawiam polecenia drobiazgi z (zauważalnie) mniej przerażającymi osobami. Dobrze jest wrócić.

aaronlevin
źródło
Myślę, że jeśli wiesz to wcześniej, że chcesz otworzyć ostatni wynik, możesz użyć czegoś takiego vim $(command |tail -n1).
varesa
Podobne pytanie zamieściłem
joelostblom

Odpowiedzi:

8

Oto potencjalne rozwiązanie problemu, które powinno być rozsądnie (ale nie całkowicie) bezpieczne w obecności funky nazw plików (nie obsługuje nazw plików z liniami - prawdopodobnie można je naprawić, ale mogą występować inne problemy).

Dwie funkcje, pierwsza działa findz przekazanymi parametrami, zapisuje dane wyjściowe w tablicy i wyświetla je. Drugi jest tylko pomocnikiem, aby uzyskać dostęp do tej tablicy.

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

Przypadek użycia:

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

Cytaty nie są wymagane, $(myget index)jeśli w nazwach plików nie ma białych znaków lub innych kłopotliwych znaków.
Przesyła całą wydajność finddo twojego środowiska, która może być ograniczona. (Użycie pliku tymczasowego zamiast tej tablicy rozwiązałoby to, ale występują inne problemy - zwłaszcza równoczesne użycie z wielu powłok).

Mata
źródło
1
Nie mogę głosować za tobą, ponieważ nie mam wystarczającej reputacji, więc oto werbalna wypowiedź: „upvote”
aaronlevin
6

Mam to w moim .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

Zasadniczo na ekranie ¬1wkleja linię nad kursorem ¬2, wkleja drugą linię nad kursorem ... i tak dalej. Możesz chcieć dodać więcej dla linii 10 i wyższych, ale stwierdzam, że po około 7 wolę używać myszy lub screentrybu kopiowania, niż licząc liczbę linii, aby uzyskać tę, którą chcę.

Stéphane Chazelas
źródło
0

innym rozwiązaniem jest: możesz napisać interaktywny skrypt, który automatycznie zapyta o twój wybór. oto kod skryptu interaktywnego:

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

zapisz ten skrypt pod dowolną nazwą, powiedz „autofind” i wywołaj skrypt za pomocą polecenia „find”, ponieważ argumentem jest kod wywołujący skrypt:

./autofind `your find command`

Ale przed użyciem skryptu sprawdź, czy polecenie „find” daje wynik, czy nie. Jeśli pokazuje jakiś wynik, użyj tylko skryptu

użytkownik1678213
źródło
0

Odpowiedź Matsa była właśnie tym, czego szukałem. Rozszerzyłem nieco jego kod, aby umożliwić więcej opcji 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
źródło