Is there a way to avoid doing grep two times in the file and just populate the variables in one pass? The file is small so it is not that a big deal I was just wondering if I could do it in one pass

FIRST_NAME=$(grep "$customer_id" customer-info|cut -f5 -d,)
LAST_NAME=$(grep "$customer_id" customer-info|cut -f6 -d,)
  • 3
    How are the variables used later in the code? It may be that they are not needed at all. – Kusalananda Mar 27 '18 at 8:13
  • 4
    @Kusalananda Why would you want to do X? Don't do X. Do Y. – user1717828 Mar 27 '18 at 12:35

You could grep once and split twice using shell string substitution:

NAME=$(grep "$customer_id" customer-info | cut -f5,6 -d,)

Or, with bash, using process substitution:

IFS=, read FIRST_NAME LAST_NAME < <(grep "$customer_id" customer-info | cut -f5,6 -d,)

read will split input on IFS and assign the first value to FIRST_NAME and the rest to LAST_NAME. Using process substitution and redirection < <(...) allows you to pass the output of grep ... | cut ... to read without using a subshell.

  • This depends on the fields being sequential. What if it changed to 5 and 7? – Jim Mar 27 '18 at 14:15
  • @Jim Then don't do the cut operation after the grep and use read to break the string by specifying a custom IFS and grab the columns you need from an array. – datUser Mar 27 '18 at 15:34
  • 1
    @Jim then cut -f5,7; or else cut -f5-7 and use ${NAME%%,*} and ${NAME##*,} (read bash's man page's section on Parameter Expansion). – drewbenn Mar 27 '18 at 15:58
  • @Jim it doesn't. Since you can specify whichever fields you want with -f - -fm,n is a list of fields, not a range - this works fine even if the fields aren't next to each other. It does require them to be in order, but if the order is reversed then just swap the variables. – Olorin Mar 27 '18 at 23:19
  • 1
    Nitpick: process substitution does use a subshell -- but it does the read in the main shell thus the variable setting is not lost, while a pipeline something | read var var can do the read in a subshell depending on your shell variant and sometimes options/modes. – dave_thompson_085 Mar 28 '18 at 1:59

The simplest would be to get the entire record into a variable and then use cut on that.

RECORD=$(grep "$customer_id" customer-info)
FIRST_NAME=$(echo "$RECORD"|cut -f5 -d,)
LAST_NAME=$(echo "$RECORD"|cut -f6 -d,)

Also personally I would recommend using a more specific regular expression. If your customer IDs are always at the beginning of the line, you can write grep '^'"$customer_id" instead of grep "$customer_id" to require the match to be at the beginning of the line. Otherwise you might pick up records where text matching the customer ID happens to appear elsewhere in the record.


You could use awk in combination with bash read:

read -r FIRST_NAME LAST_NAME <<< $(awk -F, -v cid="$customer_id" '$0~cid{print $5,$6}' customer-info)

-F tells awk to use the comma as field delimiter

-v sets the awk variable cid to the shell variable $customer_id

If the line matches the $customer_id, awk will print the 5th and 6th fields and these will get assigned the variables FIRST_NAME and LAST_NAME.

If first name ($5) ever contains space (example: a,b,c,d,Sarah Jane,Smith) add -v OFS=, to have awk output comma between fields and prefix read with IFS=, to have it split at comma.

Additionally awk can search in only a particular field like '$3~cid{print..}' -- and can match that entire field by '$3~"^"cid"$"{print...}' if that matters for your ids.

  • Doesn't work as written; edit proposed, with additions since I'm at it. – dave_thompson_085 Mar 28 '18 at 1:47
  • @dave_thompson_085 Your changes are valid one. Thanks. However I do not see any difference between the previous version having /.../ and your version $0~.... Can you also explain what did not work for you? – oliv Mar 28 '18 at 6:36
  • If you just use cid as the pattern, awk does not match the line against the regexp cid -- it only checks if the variable cid is nonempty, whilch is always true, so awk outputs all lines, not the single line you wanted. That's why you need $0 ~ cid -- match the line ($0) against the regexp in cid. – dave_thompson_085 Mar 28 '18 at 22:34
  • When you write a literal regexp /regexp/ {action} the slashes are not part of the regexp, they are special syntax that says there is a regexp. When you put the regexp in a variable if you include a slash that slash is a data character that must match the input data (here, adjacent to the customer_id value) which it almost certainly won't (although conceivably the OP could have data that always has slashes around the customer_id values). – dave_thompson_085 Mar 28 '18 at 22:35

Small file, big file. One habit I have is to always eliminate disk IO as much as I can. One way to do this is to push the file into an array. Of course this requires the env $IFS is set appropriately for the file but eliminates the IO.

data=( $(cat customer-info) )

Then you can cherry pick from that...

FIRST_NAME=$(echo "${data[@]}" | tr ' ' '\n' | grep "$customer_id" | cut -f5 -d,)

Another method might be to assign only those two bits desired to an array like so...

data=( $(grep "${customer_id}" customer-info | cut -d, -f5,6) )

  • 6
    -1 for slurping the entire data set into a Bash variable even though the filter is known beforehand. – David Foerster Mar 27 '18 at 11:44
  • 1
    How do you know the op doesn't require additional data? Assume nothing, eliminate as much overhead io as possible for future operations. Thanks for the concern and opinion however – jas- Mar 27 '18 at 20:14
  • reminds me of the guy who dumped two SQL tables into a variale each, then cross referenced them on a one to many field, then selected the single entry they needed. But I didn't know ASP, so who was I to criticise? – Grump Mar 27 '18 at 23:51
  • Weee, this was the most constructive dialog I have ever engaged in. Someone has poisoned the well. – jas- Mar 28 '18 at 8:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.