0

I want to modify a line if a specific keyword is found in a line.

CS_CODE, SM_NUM, PORT_NUM, DEV_PORT_NUM, DIRNUM, MAIN_DIRNUM, BILL_NO, ADR_NAME

In above line, if 'PORT_NUM' is present then 'BILL_NO' should be replaced with 'NA'. Only the keyword present in the line should be replaced.

I am using below approach:

while IFS=$'\n' read -r line
do
     if [[ $line == *"$string"* ]]; then
           < sed command to replace only keyword >
     fi
done < "$file"

How can I achieve this?

Note: '-i' extension does not work in my sed version.

Menon
  • 305

1 Answers1

2

Menon, you got already a sed answer how to do the operation on the whole file. If your intention is to work on individual lines, as shown in your code frame, then you don't need sed (or any other external program), since your shell can likely (see note below) do the substitution. Assuming variable string in your code contains (e.g.) the value PORT_NUM, the replacement in the line variable could be done by:

while read line
do
     if [[ $line =~ $string ]]; then
           line=${line/BILL_NO/NA}
     fi
done < "$file"

(Note: This substitution works with contemporary shells, like bash, ksh, zsh.)

Janis
  • 14,222
  • This will elide $IFS at the head and tail of each line and interpret backslash escapes in input. $line here is not literally a line from "$file" but is rather the shell's interpretation of said line. In the same vein, $string is not a literal representation of a string to match but is rather the shell's interpretation of one (or many) strings. – mikeserv Mar 31 '15 at 08:50
  • 1
    @mikeserv; your comment should certainly be put on the question! This answer is addressing the replacement, which was the OP's question, and the code frame around it taken from the original question so that he can easily see where the changes are. If I'd intend to write a solution for the OP's complete task I'd not only do the usual while IFS= read -r line, but avoid the whole shell loop. – Janis Mar 31 '15 at 08:56
  • @mikeserv; your comment "string is not a literal representation of a string to match but is rather the shell's interpretation" is strange; what do you mean here? To me string is, as I've written, a variable, whose value $string can (WRT the OP's description) here be, e.g., the pattern "PORT_NUM" to which the string(-value) is to be compared to. Please explain any issues you have with that. – Janis Mar 31 '15 at 09:12
  • Well, I mean that an unquoted var on the right hand side is a pattern and not a string. If it is just numbers then it is likely safe, but there's not validation here and so you can't know. The shell is definitely treating it as a pattern whether it is just numbers or not - except that if it is then it is a pattern which will match only one string - itself. Basically, try string=* then [[ $line =~ $string ]] and [[ $line =~ "$string" ]]. – mikeserv Mar 31 '15 at 09:23
  • @mikeserv, I see where you're comming from. Though, if the OP is searching for specific keyword patterns (like "PORT_NUM") in the lines of the file I don't see where the regexp meta-characters should come from. That said, I agree that there's quite some potential to make the snippet more robust, specifically for other application contexts, e.g. where the content of string contains uncontrolled user input, but (as already mentioned) there's likely a more appropriate way to solve the overall task anyway, so I'll try to stay focussed on the basic question asked. – Janis Mar 31 '15 at 09:43
  • I agree with you - the comment upvote is mine because what you say makes sense. I'd upvote the answer too, but... there's just too much of the question in it. I'm sorry for that - it's not the answer's fault anyway. – mikeserv Mar 31 '15 at 09:55
  • @Janis, thanks for this approach, however, the command is not making changes to the file. I am pretty sure, there is nothing wrong with file permission or condition of code. I am using bash v3.2. P.S. question edited. – Menon Mar 31 '15 at 11:42
  • @mikeserv, don't worry about your votes, it's okay. :-) – Janis Mar 31 '15 at 14:57
  • @Menon; of course the while loop does not change the file. You'd need some redirection, e.g.: change the last line to: done < "$file" > "$file.tmp" && mv "$file.tmp" "$file"; this will create a temporary file for the output and will then overwrite the original file. But as said; it would likely fit better to use an existing program, like the suggested sed approach: sed '/PORT_NUM/ s/BILL_NO/NA/' < "$file" > "$file.tmp" && mv "$file.tmp" "$file" – Janis Mar 31 '15 at 15:04
  • @Janis, I cannot believe I missed that. Now, its working fine. I am sorry, I cannot up-vote your answer since I am a new member. Regardless, thanks for your help:-) – Menon Apr 01 '15 at 07:03