Bash:在stdout中捕获/使用最后(或第N)行


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:在vim中的结果列表中打开第N个项目。

目前,我用鼠标剪切粘贴。这使我想到我的问题

  1. 是否有一种轻松的单线方式来完成选项1和2?请注意,这是find命令之后发生的。
  2. 有没有办法在某种bash向量/数组中从stdout捕获N行?

理想用法

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

(语法和语义可能有所不同,但您明白了)

相似性

似乎有几个类似的问题。这是我的感知差异(我很乐意接受启发):

谢谢您的帮助!在90年代少年时代使用* nix / BSD并打电话给倦怠的酸头邻居吓坏了,以帮助我为即插即用声卡安装驱动程序后,我对讨论命令感到欣慰,与(不太可能)令人恐惧的个人保持联系。回来感觉很好。


我想,如果您想知道之前要打开的最后一个结果,则可以使用vim $(command |tail -n1)
varesa

Answers:


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送到您的环境,这可能会受到限制。(使用临时文件而不是该数组可以解决此问题,但还有其他问题-特别是在多个shell中并发使用。)


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`

但是在使用脚本之前,请检查“查找命令”是否给出结果。如果显示结果,则仅使用脚本


0

Mats的答案正是我想要的。我扩展了他的代码,以允许更多的get-options。

$ 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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.