bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Help with script.


From: Greg Wooledge
Subject: Re: Help with script.
Date: Wed, 19 May 2010 10:29:46 -0400
User-agent: Mutt/1.4.2.3i

On Tue, May 18, 2010 at 10:08:54AM -0700, Afflictedd2 wrote:
> 
> I'm tryin to build a script that will extract columns from a comma
> sepparated or x delimited file.

And do what with them?  That matters, a lot.

> I get the following error:
> 
> Naix:Bash Naix$ ./extractCol.sh , cutDemo.input 3
> awk -F',' '{ print $3 }' < cutDemo.input
> awk: '{
> awk: ^ invalid char ''' in expression

That's because you put literal single quotes into the argument you're
giving to awk -F.  You don't want to pass literal single quotes along
with the delimiter -- you just want to pass the delimiter itself.

> #!/usr/bin/env bash
> 
> DELIMETER="'$1'"
> FILE=$2
> COLUMNS="'{ print \$$3 }'"
> 
> echo "awk -F$DELIMETER $COLUMNS < $FILE"
> awk -F$DELIMETER $COLUMNS < $FILE

You have a serious misunderstanding of how shell quoting works.  See
<http://mywiki.wooledge.org/Quotes> and
<http://mywiki.wooledge.org/BashFAQ/050>.

You've placed a literal single quote, then the first positional parameter,
then another literal single quote, into the variable DELIMETER (which is
also misspelled).  Later you're attempting to use that variable, but you
did not quote it when you used it, and therefore if $1 contains any
whitespace or glob characters, that will break.

Here is how you preserve a variable correctly:

delim="$1"
awk -F"$delim" ...

You are attempting to put part of an awk command into a variable called
COLUMNS.  Now, this is a bad idea for two very different reasons:

 * The variable COLUMNS already means something else.  It's the width
   of the terminal.  (This is why we don't use all-capital-letter variable
   names!)

 * As BashFAQ/050 says, putting code into variables is a bad idea.  If
   you really want a layer of indirection (although I don't see why you'd
   want it for something this simple) then you can isolate bits of code in
   functions.  Don't use variables.  Especially if you don't understand
   how to quote things properly -- but even if you do, it's quite difficult
   to mangle things just right so that it all works after traversing all
   the layers.

So, if I understand the question correctly, what you want is:

 * Write a program that takes three arguments.  The first argument is a
   single-character delimiter.  The second is a filename.  The third is
   a field number.

 * Extract the <field>th field from every line of <file> (as delimited
   by <delimiter>) and write them all to stdout, separated by newlines.

In pure bash:

  while IFS="$1" read -r -a array; do
    printf "%s\n" "${array[$3 - 1]}"
  done < "$2"

In bash+awk:

  awk -F"$1" -v field="$3" '{print $field}' "$2"

Yes, that's the entire script.  Slap a #!/bin/sh at the top and it's done
(we're not even using bash features in the second one; although we do in
the first one).



reply via email to

[Prev in Thread] Current Thread [Next in Thread]