Bash: Tangkap / Gunakan baris terakhir (atau Nth) di stdout

11

Pertanyaan

Saya menggunakan Bash. Ketika saya mencari file, seringkali saya akan melakukan hal berikut:

find -name stackexchange.hs

Dan seringkali hasilnya akan terlihat seperti:

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

Maka saya ingin melakukan salah satu dari yang berikut:

  • Opsi 1: Buka item terakhir dalam daftar hasil di vim .
  • Opsi 2: Buka item N di daftar hasil di vim .

Saat ini, saya memotong dan menempel dengan mouse. Yang membawa saya ke pertanyaan saya :

  1. Apakah ada yang mudah, satu-liner untuk mencapai opsi 1 & 2? Perhatikan bahwa ini terjadi setelah para findperintah.
  2. Apakah ada cara untuk menangkap garis-N dari stdout di semacam bash vector / array?

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

(sintaks dan semantik mungkin berbeda, tetapi Anda mengerti maksudnya)

Similaria

Tampaknya ada beberapa pertanyaan serupa. Inilah perbedaan yang saya rasakan (saya terbuka terhadap pencerahan):

Terima kasih untuk bantuannya! Setelah menggunakan * nix / BSD ketika saya masih remaja di tahun 90-an dan semakin takut dengan memanggil burnout, tetangga asam-kepala untuk membantu saya menginstal driver untuk kartu suara plug-and-play saya, saya lega untuk membahas perintah- berbaris kecil dengan (jelas) individu yang kurang menakutkan. Senang rasanya bisa kembali.

aaronlevin
sumber
Saya pikir, jika Anda tahu sebelumnya bahwa Anda ingin membuka hasil terakhir, Anda bisa menggunakan sesuatu seperti vim $(command |tail -n1).
varesa
Saya memposting pertanyaan serupa di sini unix.stackexchange.com/questions/348224/…
joelostblom

Jawaban:

8

Berikut adalah solusi potensial untuk masalah Anda yang seharusnya cukup (tetapi tidak sempurna) aman di hadapan nama file yang funky (tidak menangani nama file dengan linefeeds di dalamnya - mungkin dapat diperbaiki, tetapi mungkin ada masalah lain yang mengintai).

Dua fungsi, yang pertama dijalankan finddengan parameter yang Anda lewati, menyimpan output dalam array, dan menampilkannya. Yang kedua hanyalah pembantu untuk mengakses array itu.

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

Gunakan kasus:

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

Kutipan tidak diperlukan di sekitar $(myget index)jika Anda tidak memiliki spasi putih atau karakter bermasalah lainnya dalam nama file Anda.
Mendorong seluruh output findke lingkungan Anda, yang mungkin terbatas. (Menggunakan file sementara daripada array yang akan menyelesaikannya, tetapi memiliki masalah lain - penggunaan bersamaan dari beberapa shell.)

Tikar
sumber
1
Saya tidak dapat mendukung Anda karena saya tidak memiliki reputasi yang cukup, jadi ini adalah, eh, yang lisan: "upvote"
aaronlevin
6

Saya punya ini di .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

Pada dasarnya, di layar,, ¬1menempelkan garis di atas kursor ¬2,, menempelkan garis kedua di atas kursor ... dan seterusnya. Anda mungkin ingin menambahkan lebih banyak untuk baris 10 ke atas, tetapi saya menemukan bahwa setelah sekitar 7 sudah, saya lebih suka menggunakan mouse atau screenmode salin daripada menghitung jumlah baris untuk mendapatkan yang saya inginkan.

Stéphane Chazelas
sumber
0

solusi lain adalah: Anda dapat menulis skrip interaktif yang secara otomatis akan meminta pilihan Anda. di sini adalah kode untuk skrip interaktif:

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

simpan skrip ini dengan nama apa pun yang mengatakan "autofind" dan aktifkan skrip dengan "perintah temukan" Anda karena argumen di sini adalah kode untuk menjalankan skrip:

./autofind `your find command`

Tetapi sebelum menggunakan skrip periksa "temukan perintah" Anda apakah itu memberikan hasil atau tidak. Jika itu menunjukkan beberapa hasil maka hanya gunakan skrip

pengguna1678213
sumber
0

Jawaban Mats adalah apa yang saya cari. Saya telah sedikit memperluas kodenya untuk memungkinkan lebih banyak opsi 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
}
kikuk
sumber