Find the answer to your Linux question:
Results 1 to 9 of 9
Enjoy an ad free experience by logging in. Not a member yet? Register.
  1. #1
    Just Joined!
    Join Date
    May 2009
    Location
    Christchurch, NZ
    Posts
    9

    Extracting numbers between square brackets


    I'm feeling my way with bash scripting.......

    The first line of my file is:
    prmslmsl, [61][1][1]
    and I want to extract the first no in the square brackets.

    Code:
    head -n 1 GrADS.out | cut -d\, -f 2 | cut -d\] -f 1 | cut -d\[ -f 2
    gives me the answer, but it's very ugly.

    Is there a better way?

  2. #2
    if you have Python
    Code:
    # python -c "first=open('file').readline();print first.split('[')[1][:-1]"
    61
    awk
    Code:
    awk -F"[" 'NR==1{sub("]","",$2);print $2}'  file

  3. #3
    Just Joined!
    Join Date
    May 2009
    Location
    Oregon
    Posts
    51
    Depends on what "neat" is... using bash alone, I would do:
    x=`head -n 1 GrADS.out`; x=${x#*[}; x=${x%%]*}; echo $x

    Or, there is always sed --- available on just about all UNIX's.
    Assuming there are no numbers before the first one, you can do:
    head -n 1 GrADS.out | sed -ne "s%[^0-9]*\([0-9]*\).*%\1%p"

    What's your preference?

  4. $spacer_open
    $spacer_close
  5. #4
    Quote Originally Posted by andrewr View Post
    Depends on what "neat" is... using bash alone, I would do:
    x=`head -n 1 GrADS.out`; x=${x#*[}; x=${x%%]*}; echo $x
    well if you use head, that's not considered bash alone
    Code:
    read x < "GrADS.out"

  6. #5
    Just Joined!
    Join Date
    May 2009
    Location
    Oregon
    Posts
    51
    aye, I wasn't specific -- I meant matching by bash alone....
    And you see a workaround .... too.

    or how about....

    read -d ] x < GrADS.out; echo ${x#*[}

    OOOOHHHHH.... or, do an

    IFS="[]"; read -a x < GrADS.out; echo ${x[1]}

    (could be executed in a subshell to save saving IFS and restoring... recurse.. curse...
    ....

    Too bad sed doesn't allow positive matches .... it would make it so much more readable.



    Oh, and since I cant sleep -- & my thread question isn't getting answers -- how about one that avoids your solution (read) but is more FLEXIBLE!!! USING TRULY ONLY BaSh!!!
    (Pick a line, any line.... !1 = line 1, !2=line 2, etc....

    x=( `bash -c 'history -cr GrADS.out; history -p "!1:gs/[/ /:gs/]//"' )
    echo ${x[1]}

    oddly, bash -c is required, as merely executing in a subshell does NOT reset the current history line to 0 -- a parenthetical inside the `( history -cr ... )` inherits the present history line number.
    Unfortunately, I don't see a way to reset BASH's notion of the present history line number without calling bash, directly.... so, it is slower than a mere fork() -- challenge Q: does anyone know how to make that work without the bash -c ??? :shrug:
    Last edited by andrewr; 05-29-2009 at 09:44 AM. Reason: add a perverted way....

  7. #6
    Just Joined!
    Join Date
    May 2009
    Location
    Christchurch, NZ
    Posts
    9
    Thanks you blokes.

    For me, the neatest is:

    read -d ] x < GrADS.out; echo ${x#*[}

    Now, I'm off to find out how it works exactly...............

  8. #7
    Just Joined!
    Join Date
    May 2009
    Location
    Christchurch, NZ
    Posts
    9
    I'm trying to understand this:
    read -d ] x < GrADS.out; echo ${x#*[}

    OK, I understand this bit:
    read -d ] x < GrADS.out | echo $x
    It gives:
    prmslmsl, [61

    First of all, why do we use ;, not | for the next bit? Either seems to work OK.

    And I guess ${x#*[} means retain all the characters after the [
    Where could I learn this stuff?

  9. #8
    Just Joined!
    Join Date
    May 2009
    Location
    Oregon
    Posts
    51
    Quote Originally Posted by TideMan View Post
    I'm trying to understand this:
    read -d ] x < GrADS.out; echo ${x#*[}

    OK, I understand this bit:
    read -d ] x < GrADS.out | echo $x
    It gives:
    prmslmsl, [61

    First of all, why do we use ;, not | for the next bit? Either seems to work OK.

    And I guess ${x#*[} means retain all the characters after the [
    Where could I learn this stuff?
    Actually, I didn't use | between the commands because echo does not use std input.
    the semicolon ; simply means the same as an enter button. It allows multiple unrelated/unpiped commands on one line.
    You can get away with | because echo ignores the standard input and isn't confused by it.
    I like your use of | better, and wish I had thought of that twisted thinking for 1 reason only -- BASH has bugs in job control (see the end of the man file -- under BUGS). Type: man bash
    the | would prevent that problem .... hmmm.... neat.

    for non-bug learning; With the manual up type:
    /Parameter Expansion
    followed by enter.

    That will search for the section explaining how to remove parts of variables.
    Actually, ${x#*[} is negative thinking only -- remove everything up to and including the [

    Cheers.

    P.S.
    Sheeesh.... found another BASH BUG. Anyone know why this *DOESNT* work?

    read -d ] < GrADS.out | echo ${REPLY#*[}

    What is wrong with it, logically?
    If nothing, could someone write to the bug list of whoever maintains BASH.
    It has lots of quirks....
    Last edited by andrewr; 05-29-2009 at 10:23 AM. Reason: help, I'm editing and can't stooooooppppp.....

  10. #9
    Just Joined!
    Join Date
    May 2009
    Location
    Christchurch, NZ
    Posts
    9
    Thank you for your help, andrewr
    ${x#*[} seems like gobbledygook at first, but as I read the manual, it means take x and remove (#) everything (*) up to and including the [, which leaves 61.

    Actually, I might revise my assessment of elegance and go for:
    IFS="[]"; read -a x < GrADS.out; echo ${x[1]}
    having just learned about IFS

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •