Bash tool to get nth line from a file

前端 未结 19 2071
刺人心
刺人心 2020-11-22 08:07

Is there a \"canonical\" way of doing that? I\'ve been using head -n | tail -1 which does the trick, but I\'ve been wondering if there\'s a Bash tool that speci

相关标签:
19条回答
  • 2020-11-22 08:42

    As a followup to CaffeineConnoisseur's very helpful benchmarking answer... I was curious as to how fast the 'mapfile' method was compared to others (as that wasn't tested), so I tried a quick-and-dirty speed comparison myself as I do have bash 4 handy. Threw in a test of the "tail | head" method (rather than head | tail) mentioned in one of the comments on the top answer while I was at it, as folks are singing its praises. I don't have anything nearly the size of the testfile used; the best I could find on short notice was a 14M pedigree file (long lines that are whitespace-separated, just under 12000 lines).

    Short version: mapfile appears faster than the cut method, but slower than everything else, so I'd call it a dud. tail | head, OTOH, looks like it could be the fastest, although with a file this size the difference is not all that substantial compared to sed.

    $ time head -11000 [filename] | tail -1
    [output redacted]
    
    real    0m0.117s
    
    $ time cut -f11000 -d$'\n' [filename]
    [output redacted]
    
    real    0m1.081s
    
    $ time awk 'NR == 11000 {print; exit}' [filename]
    [output redacted]
    
    real    0m0.058s
    
    $ time perl -wnl -e '$.== 11000 && print && exit;' [filename]
    [output redacted]
    
    real    0m0.085s
    
    $ time sed "11000q;d" [filename]
    [output redacted]
    
    real    0m0.031s
    
    $ time (mapfile -s 11000 -n 1 ary < [filename]; echo ${ary[0]})
    [output redacted]
    
    real    0m0.309s
    
    $ time tail -n+11000 [filename] | head -n1
    [output redacted]
    
    real    0m0.028s
    

    Hope this helps!

    0 讨论(0)
  • 2020-11-22 08:44

    head and pipe with tail will be slow for a huge file. I would suggest sed like this:

    sed 'NUMq;d' file
    

    Where NUM is the number of the line you want to print; so, for example, sed '10q;d' file to print the 10th line of file.

    Explanation:

    NUMq will quit immediately when the line number is NUM.

    d will delete the line instead of printing it; this is inhibited on the last line because the q causes the rest of the script to be skipped when quitting.

    If you have NUM in a variable, you will want to use double quotes instead of single:

    sed "${NUM}q;d" file
    
    0 讨论(0)
  • 2020-11-22 08:44

    After taking a look at the top answer and the benchmark, I've implemented a tiny helper function:

    function nth {
        if (( ${#} < 1 || ${#} > 2 )); then
            echo -e "usage: $0 \e[4mline\e[0m [\e[4mfile\e[0m]"
            return 1
        fi
        if (( ${#} > 1 )); then
            sed "$1q;d" $2
        else
            sed "$1q;d"
        fi
    }
    

    Basically you can use it in two fashions:

    nth 42 myfile.txt
    do_stuff | nth 42
    
    0 讨论(0)
  • 2020-11-22 08:45

    If you got multiple lines by delimited by \n (normally new line). You can use 'cut' as well:

    echo "$data" | cut -f2 -d$'\n'
    

    You will get the 2nd line from the file. -f3 gives you the 3rd line.

    0 讨论(0)
  • 2020-11-22 08:47

    Lots of good answers already. I personally go with awk. For convenience, if you use bash, just add the below to your ~/.bash_profile. And, the next time you log in (or if you source your .bash_profile after this update), you will have a new nifty "nth" function available to pipe your files through.

    Execute this or put it in your ~/.bash_profile (if using bash) and reopen bash (or execute source ~/.bach_profile)

    # print just the nth piped in line
    nth () { awk -vlnum=${1} 'NR==lnum {print; exit}'; } 
    

    Then, to use it, simply pipe through it. E.g.,:

    $ yes line | cat -n | nth 5
         5  line
    
    0 讨论(0)
  • 2020-11-22 08:49

    I've put some of the above answers into a short bash script that you can put into a file called get.sh and link to /usr/local/bin/get (or whatever other name you prefer).

    #!/bin/bash
    if [ "${1}" == "" ]; then
        echo "error: blank line number";
        exit 1
    fi
    re='^[0-9]+$'
    if ! [[ $1 =~ $re ]] ; then
        echo "error: line number arg not a number";
        exit 1
    fi
    if [ "${2}" == "" ]; then
        echo "error: blank file name";
        exit 1
    fi
    sed "${1}q;d" $2;
    exit 0
    

    Ensure it's executable with

    $ chmod +x get
    

    Link it to make it available on the PATH with

    $ ln -s get.sh /usr/local/bin/get
    

    Enjoy responsibly!

    P

    0 讨论(0)
提交回复
热议问题