Simon
Simon

Reputation: 751

Replace newline and variable in text file

I have the following file ~/hello.txt:

qwe asd qwe
bash "$HOME/byobu.sh"
bash "$HOME/byobu.sh"
# asd yxc asd

How can I replace a string beginning with a newline and a variable so that the file will result in:

qwe asd qwe
# bash "$HOME/byobu.sh"
bash "$HOME/byobu.sh"
# asd yxc asd

i.e. replace the first occurrence of the second string to its commented version. Trying the following commands:

byobu_line='bash "$HOME\/byobu.sh"'
sed -i '0,/\n'"$byobu_line"'/ s/\n'"$byobu_line"'/\n# '"$byobu_line"'/' ~/hello.txt

leaves the file untouched.

Solutions without sed are accepted as well.

Note: the newline \n here is necessary to be matched and replaced by itself because, if I run it a second time without it, it will lead to the unwanted result

qwe asd qwe
# # bash "$HOME/byobu.sh"
bash "$HOME/byobu.sh"
# asd yxc asd

Upvotes: 3

Views: 243

Answers (2)

RavinderSingh13
RavinderSingh13

Reputation: 133498

Could you please try following, if you are ok with awk.

byobu_line='bash "$HOME/byobu.sh"'
awk -v var="$byobu_line" '$0 == var && !found{$0="#"$0;found=1} 1' Input_file


EDIT: To avoid expansion of $HOME I have come up with a thought, could you please try following, will add explanation in sometime(after having my dinner :) ).

Here is the Input_file:

cat file
qwe asd qwe
bash "$HOME/byobu.sh"
bash "$HOME/byobu.sh"
# asd yxc asd

Here is the script:

cat file.ksh
HOME="/home/ubuntu"    ##Setting value for testing purposes, to make sure they are NOT getting expanded.
byobu_line='bash "$HOME/byobu.sh"'
byobu_line_new=$(echo "${byobu_line//$/}")
awk -v var="$byobu_line_new" 'BEGIN{sub(/HOME/,"$&",var)} $0 == var && !found{$0="#"$0;found=1} 1' Input_file

Please append > temp && mv temp Input_file in my awk code's line above and it will save output into Input_file itself.

Explanation: EDIT code what I am doing is, I am using shell's parameter expansion to create variable named byobu_line_new which DO NOT have $. Now when same variable is passed to awk in var variable and before reading Input_file in BEGIN section I again used substitution to get $ before HOME. This is only to remove affect of expansion of variable(as a work around).

For awk code: I have passed shell variable byobu_line_new to awk variable named var, then in BEGIN section I am substituting HOME with $HOME in variable(which I previously removed mentioned above), now when Input_file is getting read checking condition if $0(current line) is equal to var and variable found is NOT SET(which will happen only for very first match, because when a match of var==$0 is found means your shell variable is found in line then setting variable found to 1, so next time this condition will NOT be TRUE). Then mentioning 1 will print edited/non-edited lines from Input_file(since awk works on method of pattern and then action, I am making condition TRUE by mentioning 1 and no action given so by default print of line will occur).

Upvotes: 5

David C. Rankin
David C. Rankin

Reputation: 84561

Your attempt at the use of sed was very close. The use of the form 0,/match/s/find/replace/ was correct, but you need to alter it slightly as:

sed '0,/^bash\ "\/home\/david\/byobu.sh"$/s/^bash\ "\/home\/david\/byobu.sh"$/# bash\ "\/home\/david\/byobu.sh"$/' file

The use of the 0,addr2 causes sed to start out in the "matched first address" state so when addr2 is found the expression will be at the end of its range, make the substitution once and then not match again having the effect of matching the first occurrence only.

You were simply mistaken in believing you needed to search for a line beginning with '\n'. That isn't the way sed works. Lines don't begin with '\n' characters.

In the expression bash "/home/david/byobu.sh", you should anchor the beginning and end with '^' and '$', respectively so you do not also match a lesser included substring of a larger line.

For the substitution, you simply need to insert "# " at the beginning of the line with s/^bash\ "\/home\/david\/byobu.sh"$/# bash\ "\/home\/david\/byobu.sh"$/. For the same reason you need to anchor the 0,/addr2/ term, you should fully describe and anchor the substitution terms as well.

Putting it altogether, you could do:

$ sed '0,/^bash\ "\/home\/david\/byobu.sh"$/s/^bash\ "\/home\/david\/byobu.sh"$/# bash\ "\/home\/david\/byobu.sh"$/' file
qwe asd qwe
# bash "/home/david/byobu.sh"
bash "/home/david/byobu.sh"
# asd yxc asd

Which provides your expected results.

Upvotes: 3

Related Questions