Bash: захват / использование последней (или N-й) строки в stdout

11

запрос

Я использую Bash. Когда я ищу файлы, часто я делаю следующее:

find -name stackexchange.hs

И часто результаты будут выглядеть так:

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

Тогда я хочу сделать одно из следующего:

  • Вариант 1. Откройте последний элемент в списке результатов в vim .
  • Вариант 2. Откройте элемент N в списке результатов в vim .

В настоящее время я вырезал и вставлял мышью. Что подводит меня к моему вопросу :

  1. Есть ли простой однострочный вариант для выполнения вариантов 1 и 2? Обратите внимание , что это происходит после того, как в findкоманде.
  2. Есть ли способ захвата N-строк из стандартного вывода в некотором виде вектора / массива bash?

Идеальное использование

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

(синтаксис и семантика могут отличаться, но вы понимаете, в чем дело)

Similaria

Кажется, есть несколько похожих вопросов. Вот мои предполагаемые различия (я открыт для просветления):

Спасибо за помощь! Воспользовавшись * nix / BSD, когда я был подростком в 90-х годах, и испугался, позвонив своему соседу, утомленному кислотой, чтобы помочь мне установить драйверы для моей звуковой карты «подключи и работай», я с облегчением обсудил мелочи линии с (заметно) менее пугающими людьми. Приятно вернуться.

aaronlevin
источник
Я думаю, что если вы знаете это раньше, что вы хотите открыть последний результат, вы могли бы использовать что-то вроде vim $(command |tail -n1).
Вареса
Я разместил подобный вопрос здесь unix.stackexchange.com/questions/348224/…
joelostblom

Ответы:

8

Вот потенциальное решение вашей проблемы, которое должно быть достаточно (но не идеально) безопасным при наличии нестандартных имен файлов (не обрабатывает имена файлов с переводами строк в них - возможно, это можно исправить, но могут быть и другие скрытые проблемы).

Две функции, первая из которых работает findс параметрами, которые вы передаете, сохраняет выходные данные в массив и отображает их. Второй - просто помощник для доступа к этому массиву.

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

Случай использования:

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

Кавычки не обязательны, $(myget index)если в именах файлов нет пробелов или других проблемных символов.
Переносит весь вывод findв вашу среду, которая может быть ограничена. (Использование временного файла вместо этого массива решило бы это, но имеет другие проблемы - особенно одновременное использование из нескольких оболочек.)

Мат
источник
1
Я не могу отказать вам, потому что мне не хватает репутации, поэтому вот словесная: «upvote»
aaronlevin
6

У меня есть это в моем .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

Обычно на экране ¬1вставляет строку над курсором ¬2, вставляет вторую строку над курсором ... и так далее. Возможно, вы захотите добавить больше для строк 10 и выше, но я обнаружил, что уже после 7 я предпочел бы использовать мышь или screenрежим копирования, чем подсчитывать количество строк, чтобы получить ту, которую я хочу.

Стефан Шазелас
источник
0

Другое решение: вы можете написать интерактивный скрипт, который автоматически запросит ваш выбор. Вот код для интерактивного скрипта:

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

сохраните этот скрипт под любым именем, скажем «autofind», и вызовите скрипт с помощью «find command», в качестве аргумента здесь используется код для вызова скрипта:

./autofind `your find command`

Но перед использованием сценария проверьте свою команду «find», дает ли она результат или нет. Если он показывает какой-либо результат, используйте только сценарий.

user1678213
источник
0

Ответ Матса был именно тем, что я искал. Я немного расширил его код, чтобы получить больше опций 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
источник