1

I am trying to write a shell script to search a cell value in a csv file and replace a corresponding cell in the same row.

Below is my csv file (report.csv)

#,Device,Status
1,Device1,Used
2,Device2,Free
3,Device3,Free

This is want I wanted to do:

User will enter device name as input while executing the script.

e.g., Enter the device name:
user enters "Device2"

Next step: script should search for Device2 in report.csv. If it finds Device2, check the value in the status column corresponding to it and, if the value is Free, replace it as Used, else exit the script saying Device2 already in use.

I have tried with following code

#!/bin/bash
dvc=""
FILE_HOME="/tmp/test/"

update_value () {  
  awk -F, -v d="$dvc" '$2==d{if($3=="Free")$3="Used"; else print d "In use"; input_value;}1' \
      OFS=, "$FILE_HOME"/report.csv > "$FILE_HOME"/report1.csv  
  mv "$FILE_HOME"/report1.csv "$FILE_HOME"/report.csv
}

input_value () {
  echo -e "Enter the device name:\n"
  read dvc
  update_value
}

input_value

This code is fine when the if statement is true.
But when the if statement is false, it is not calling function input_value from the else section.  It just prints Device# In use along with other entries in csv file to new file.

I get below output files

  • when input is Device2:

    #,Device,Status
    1,Device1,Used
    2,Device2,Used
    3,Device3,Free

  • when input is Device1:

    #,Device,Status
    Device1 In use
    1,Device1,Used
    2,Device2,Free
    3,Device3,Free

Is this the right approach I am doing?
I would be happy to see if there a different/better approach to achieve the requirement.

closed as primarily opinion-based by αғsнιη, Stephen Rauch, Archemar, Anthon, Rui F Ribeiro Aug 3 '17 at 7:56

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.

  • the other post: unix.stackexchange.com/questions/381900/… – Jeff Schaller Aug 2 '17 at 13:52
  • you can edit the other post to clarify it – Jeff Schaller Aug 2 '17 at 13:52
  • 1
    Be aware that your approach is not safe in a multitasking environment. With two users executing the script in parallel, strange things may happen. In a productive system, you may want some mutex mechanism like setlock – Philippos Aug 2 '17 at 14:04
  • 1
    (1) You're having the problem that you're having because awk can't call shell functions. (2) Why are you trying to call input_value from the awk code? Your statement of your requirements / objectives says “if the value is Free, replace it as Used, else exit the script ” (emphasis added), yet it looks like you have written the code to stay in the script and ask the user for another device name. Please decide what you want and express it clearly. – Scott Aug 2 '17 at 20:04
  • 1
    VTC’ing as POB just because the question asks “Is this the right approach I am doing?” and asks about “a better approach” seems like an awfully petty thing to do to a new user with a real question. (Is there something else about this question that makes it opinion-based? I don’t see anything.) – Scott Aug 2 '17 at 20:45
1

Write your error message to stderr.

awk -F, OFS=, -v d="$dvc" '
    $2==d {
      if($3=="Free") $3="Used"
      else print d "In use" > "/dev/stderr"
      print
    }' $FILE_HOME/report.csv > $FILE_HOME/report1.csv

This will make it appear on screen (if stderr is not redirected), rather than in the standard output.

That being said, there are things you could do to make your script a bit simpler to work with. How about this:

#!/usr/bin/awk -f
BEGIN {
  FS=","
  OFS=","
  FILE_NAME="/tmp/test/report1.csv"
  print "Enter the device name:"
  getline d < "-"
}

$2==d && $3=="Free" {
  $3="Used"
  print > FILE_NAME
  next
}

$2==d {
  print d " in use" > "/dev/stderr"
}

{
  print > FILE_NAME
}

Invoke it with

$ script_name /tmp/test/report.csv

and/or wrap it in bash as you please.

  • 1
    (1) You set FILE_NAME and never use it, and you use file1 and never set it.  I guess you got your wires crossed?  (2) Your script never writes to the standard output.  Why do you hardcode an output filename in the awk code, when you could much more flexibly have a shell script redirect the standard output from awk? – Scott Aug 2 '17 at 20:06
  • @Scott (1) I did, thanks. (2) - well this was an awk script intended as a replacement of the bash script in the question - where the file name is hard coded. The script does write to stdout, in the BEGIN clause. – Bex Aug 3 '17 at 6:29
  • 1
    Oops; I overlooked that. – Scott Aug 3 '17 at 7:02
  • @Bex: looks like your script done the work for me :). Thanks for the help. Let me do some more analysis with that and come back if get struck.. Thanks once again :) – sajesh pp Aug 4 '17 at 12:50
1

Are you limited to shell and awk only? Do you able to use SQLite to store your data or at least to parse it?

sqlite> .mode csv
sqlite> .import report.csv report
sqlite> update report set status="Used" where Device="Device2";
sqlite> select * from report;
 1,Device1,Used
 2,Device2,Used
 3,Device3,Free

Or may be you can use more general programming languages like Python for your task?

import csv

with open('report.csv') as csvfile:
  reader = csv.DictReader(csvfile)
  for row in reader:
    if row['Device'] == "Device3":
      row['Status'] = "Used"
    print row

{'Device': 'Device1', 'Status': 'Used', '#': '1'}
{'Device': 'Device2', 'Status': 'Free', '#': '2'}
{'Device': 'Device3', 'Status': 'Used', '#': '3'}
  • Thanks for your suggestion. Since I do not have knowledge on SQLite and Python, I prefer to do with shell and awk as I am more comfortable with those. – sajesh pp Aug 4 '17 at 12:53

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