Suggestions for command line parsing


 
Thread Tools Search this Thread
Top Forums Shell Programming and Scripting Suggestions for command line parsing
# 1  
Old 06-23-2013
Suggestions for command line parsing

Hi all

I need to put a command line parser together to parse numeric fields and ranges passed to a script. I'm looking for a bash function that is as elegant and simple as possible.

So the input would be of the following form -
Code:
1,2,8-12

This would return -
Code:
1,2,8,9,10,11,12

Input can contain multiple positive or negative ranges of the form -
Code:
9-12,19-15

in any order with comma delimited single integers interspersed anywhere on the line.

I've come up with some code but frankly its ugly and I suspect that this is a problem that's been solved before.

Does anyone have a good solution?

Brad
# 2  
Old 06-23-2013
Here is a bash approach:
Code:
#!/bin/bash

while read line
do
        for num in ${line//,/ }
        do
                if [[ "$num" =~ \- ]]
                then
                        if [[ "$num" =~ ^\- ]]
                        then
                                st="${num%-*}"
                                [[ "$st" =~ \-$ ]] && st="${st%-*}"
                                en="${num#*-}"
                                [[ "$en" =~ [0-9]*-[0-9]* ]] && en="${en#*-}"
                        else
                                st=${num%%-*}
                                en=${num#*-}
                        fi
                        if [ $st -le $en ]
                        then
                                while [ $st -le $en ]
                                do
                                        [ -z "$tmp" ] && tmp="$st" || tmp="$tmp $st"
                                        (( st++ ))
                                done
                        else
                                while [ $st -ge $en ]
                                do
                                        [ -z "$tmp" ] && tmp="$st" || tmp="$tmp $st"
                                        (( st-- ))
                                done
                        fi
                else
                        if [ -z "$tmp" ] && [ ! -z "$num" ]
                        then
                                tmp="$num"
                        elif [ ! -z "$num" ]
                        then
                                tmp="$tmp $num"
                        fi
                fi
        done

        printf "%s\n" "${tmp// /,}"
        tmp=""

done < file

Input & Output:
Code:
$ cat file
1,2,8-12,15,20-25
9-12,19-15,20-20,0--5,-1-2,-5--1

$ ./scr
1,2,8,9,10,11,12,15,20,21,22,23,24,25
9,10,11,12,19,18,17,16,15,20,0,-1,-2,-3,-4,-5,-1,0,1,2,-5,-4,-3,-2,-1

These 3 Users Gave Thanks to Yoda For This Post:
# 3  
Old 06-24-2013
Very Nice Yoda

That's much better than anything I've come up with so far.

Many thanks, I'm sure a few people will find that useful.

Brad
# 4  
Old 06-24-2013
A Perl solution :
Code:
#!/usr/bin/perl -w
use strict;

my $cur_dir = $ENV{PWD};
my ($value,@values,@chars,@sep,$i,@ext,$idx);
my ($startVal,$endVal,$secNegFlg);

@values=split(/,/,$ARGV[0]);

foreach $value (@values) {
  $idx++;
  @chars=split(//,$value);
  @sep=grep{ $_ eq "-" } @chars;

  #one or more (3 max) "-" found -> range format (xx-xx) detected
  # define starting and ending values of range
  if ($#sep >= 0) {
    # if 1st char is -, first number of range is negative
    if ($chars[0] eq "-") { ($startVal) = $value =~ m/^(-\d+)/ }
      else { ($startVal) = $value =~ m/^(\d+)/ } ;

    # if -- found, second number of range is negative
    ($secNegFlg) = $value =~ m/(--\d+)$/;
    if( defined($secNegFlg) ) {  ($endVal) = $value =~ m/(-\d+)$/ }
      else { ($endVal) = $value =~ m/(\d+)$/ } ;
  }

  # Printing stage
  if ($#sep >= 0) {
    if ($startVal > $endVal) {
      for($i=$startVal; $i>=$endVal; $i--) {
        print "$i";
        print "," if( $i > $endVal);
      }
    } else {
      for($i=$startVal; $i<=$endVal; $i++) {
        print "$i";
        print "," if( $i < $endVal);
      }
    }
    print "," if( $idx <= $#values);
  } else {
    print "$value";
    print "," if( $idx <= $#values);
  }
}

print "\n";

Output :
Code:
 %./file034.pl 1,3-5,10,15-17,20,-25--22,0--3,-2-2

1,3,4,5,10,15,16,17,20,-25,-24,-23,-22,0,-1,-2,-3,-2,-1,0,1,2

# 5  
Old 06-24-2013
And another one:
Code:
perl -le'
  ($_ = shift) =~ 
    s/(-?\d+)-(-?\d+)/
    join ",", $1 > $2 ? reverse @{[$2..$1]} : @{[$1..$2]}
    /xeg;
  print
  ' -- <your_string>

Code:
% perl -le'
  ($_ = shift) =~
    s/(-?\d+)-(-?\d+)/
    join ",", $1 > $2 ? reverse @{[$2..$1]} : @{[$1..$2]}
    /xeg;
  print
  ' -- 9-12,19-15,20-20,0--5,-1-2,-5--1
9,10,11,12,19,18,17,16,15,20,0,-1,-2,-3,-4,-5,-1,0,1,2,-5,-4,-3,-2,-1


Last edited by radoulov; 06-24-2013 at 05:11 PM..
# 6  
Old 06-24-2013
Works in recent bash, but not for negative numbers:
Code:
T=1,4,5,6-10,13-25
IFS="," A=($T) 
for  ((I=0; I<${#A[@]}; I++ )); do [ ${A[I]/-} = ${A[I]} ] &&  echo -n ${A[I]} || eval echo -n {${A[I]/-/..}}; echo -n " "; done; echo
1 4 5 6 7 8 9 10 13 14 15 16 17 18 19 20 21 22 23 24 25

You could also read variable T from a file in a loop...

EDIT: Actually, with a minor modification, we're getting quite far into the negatives:
Code:
T=1,-4,5,6--10,13-25
IFS="," A=($T) 
for ((I=0; I<${#A[@]}; I++ )); do [ ${A[I]/?-} = ${A[I]} ] && echo -n ${A[I]} || eval echo -n {${A[I]/-/..}}; echo -n " "; done; echo
1 -4 5 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 13 14 15 16 17 18 19 20 21 22 23 24 25


Last edited by RudiC; 06-24-2013 at 04:59 PM..
# 7  
Old 06-24-2013
Thanks for the perl examples.

I need to pull this off in bash so I can't use them but they certainly look powerful. The last example being particularly compact...

Cheers

---------- Post updated at 10:35 PM ---------- Previous update was at 10:21 PM ----------

this is the best I have managed to come up with. Doesn't handle negative numbers but that was never in my original requirement -

Code:
#! /bin/bash

error_exit()
{
    echo $1
    exit 1
}

expand_range()
{
    [[ $@ =~ ^[0-9]*-[0-9]*$ ]] || error_exit "Usage: args ${FUNCNAME}"
    local LA=${1#*-} FA=${1%-*}
    local TEST="\(\(FA++\)\) -lt \${LA}"
    [[ ${FA} -gt ${LA} ]] && TEST="\(\(FA--\)\) -gt \${LA}"

    OUT="${OUT},${FA}"
    while eval [[ ${TEST} ]]
    do
        OUT="${OUT},${FA}"
    done
}

parse_line()
{
    [[ $@ =~ ^[0-9]+[0-9,-]*[0-9-]+$ ]] || error_exit "Usage: args ${FUNCNAME}"
    OUT=
    for SEG in $(echo $@ | awk 1 RS=",")
    do
        if [[ ${SEG} =~ ^[0-9]*-[0-9]*$ ]]
        then
            expand_range ${SEG}
        else
            OUT="${OUT},${SEG}"
        fi
    done
    echo ${OUT#,*}
}

parse_line 2,15-9,3,5-8,1

Output -

Code:
2,15,14,13,12,11,10,9,3,5,6,7,8,1

---------- Post updated at 10:51 PM ---------- Previous update was at 10:35 PM ----------

Well I'm glad to see so many people enjoying the problem as much as I am .. Smilie
Login or Register to Ask a Question

Previous Thread | Next Thread

10 More Discussions You Might Find Interesting

1. Shell Programming and Scripting

Parsing XML using command line

Hi Experts, How do I parse a XML with below contents <saw:user name="mbussey@xyz.com" /> <saw:user name="kimmy.chan@pqr.com" /> <saw:user name="chudgins@gmail.com" /> and retrieve below output ? mbussey@xyz.com kimmy.chan@pqr.com chudgins@gmail.com ... (17 Replies)
Discussion started by: pauldx
17 Replies

2. Shell Programming and Scripting

Command Line Perl for parsing fasta file

I would like to take a fasta file formated like >0001 agttcgaggtcagaatt >0002 agttcgag >0003 ggtaacctga and use command line perl to move the all sample gt 8 in length to a new file. the result would be >0001 agttcgaggtcagaatt >0003 ggtaacctga cat ${sample}.fasta | perl -lane... (2 Replies)
Discussion started by: jdilts
2 Replies

3. Shell Programming and Scripting

awk command suggestions

I've defined the order of elements which needs to be print in a order in a variable. please let me know how can I use it in awk command ... (2 Replies)
Discussion started by: BrahmaNaiduA
2 Replies

4. Shell Programming and Scripting

Parsing Command Line Arguments In C shell script

]I have a string like "/abc/cmind/def/pq/IC.2.4.6_main.64b/lnx86" and this string is given by user. But in this string instead of 64b user may passed 32 b an i need to parse this string and check wether its is 32b or 64 b and according to it i want to set appropriate flags. How will i do this... (11 Replies)
Discussion started by: codecatcher
11 Replies

5. Programming

Parsing command line arguments in Python

Hi, I've a python script called aaa.py and passing an command line option " -a" to the script like, ./aaa.py -a & Inside the script if the -a option is given I do some operation if not something else. code looks like ./aaa.py -a . . if options.a ---some operation--- if not options.a... (1 Reply)
Discussion started by: testin
1 Replies

6. Shell Programming and Scripting

Parsing a command line parameter in script

I have a simple script that builds a complex program call which passes a number of parameters to the program. I'm trying to enhance the script to include the value of the command line parameter in the name of a file being created. The problem I'm having is that the parameter may include a forward... (11 Replies)
Discussion started by: pbmax626
11 Replies

7. Shell Programming and Scripting

Help parsing command line arguments in bash

Looking for a little help parsing some command line arguments in a bash script I am working on, this is probably fairly basic to most, but I do not have much experience with it. At the command line, when the script is run, I need to make sure the argument passed is a file, it exists in the... (3 Replies)
Discussion started by: Breakology
3 Replies

8. Shell Programming and Scripting

parsing command line switches in Perl

Hi, My perl script takes few switches which i'm parsing through GetOpt::Long module. My script looks like something : myscript.pl --file="foo" --or --file="bar" The --file switch takes 2 arguments foo and bar. The 2 values of file are separated by --or switch. I want to ensure that... (1 Reply)
Discussion started by: obelix
1 Replies

9. UNIX for Dummies Questions & Answers

command line argument parsing

how to parse the command line argument to look for '@' sign and the following with '.'. In my shell script one of the argument passed is email address. I want to parse this email address to look for correct format. rmjoe123@hotmail.com has '@' sign and followed by a '.' to be more... (1 Reply)
Discussion started by: rmjoe
1 Replies

10. UNIX for Advanced & Expert Users

Parsing the command line arguments

Is there a way to get the command line arguments. I am using getopt(3) but if the arguments are more than one for a particular option than it just ignores the second argument. For eg ./a.out -x abc def now abd will be got with -x using getopt "( x : )" and string abc\0def will get stored... (7 Replies)
Discussion started by: jayakhanna
7 Replies
Login or Register to Ask a Question