2

I'm writing my own password manager, which I want to be strictly POSIX compliant. To simplify: I have this file, file.csv:

mastodon;password2
bla;test
test;test
posteo;tewtasdwqrr

Basically, I want to have a function which takes two arguments: it should select the first argument from the first column and from that line, it should replace the second column entry with the second argument.

For instance: f "bla" "etewtw" would change the password of the last entry to etewtw.

I tried to use awk, which also somewhat works:

awk -F ";" -v acc="bla" -v newpw="etewtw" \
'$1 ~ acc { $2=newpw; print $1";"$2 } END {print;} ' file.csv

Basically, I tried to set the second column to the newpw argument if the first column matches on the acc argument. After changing the stream, I want to print the complete stream, which doesn't work. The above is obviously not the correct solution, but I don't know how to fix that.

The output is:

bla;etewtw
posteo;tewtasdwqrr

So it is kinda successful, the entry was changed (at least in the stream, but actually changing the file is not difficult).

However, two problems arise:

  1. Entries are missing in the output. Namely mastodon;password2 and test;test. I expect those to a) stay at the same line and b) be unchanged.

  2. If I want to change the last line, it is always wrong. For instance, if I use awk -F ";" -v acc="posteo" -v newpw="test" '$1 ~ acc { $2=newpw; print $1";"$2 } END {print;} ' instead, the result is:

posteo;test
posteo test

which is not what I want. I want the last line to be no different to any other line.

2 Answers 2

6

You need either of these to have your name and password treated as literal strings:

acc='bla' newpw='etewtw' awk '
    BEGIN{FS=OFS=";"; acc=ENVIRON["acc"]; newpw=ENVIRON["newpw"]}
    $1 == acc{$2=newpw} 1
' file.csv

awk '
    BEGIN{FS=OFS=";"; acc=ARGV[1]; newpw=ARGV[2]; ARGV[1]=ARGV[2]=""}
    $1 == acc{$2=newpw} 1
' 'bla' 'etewtw' file.csv

You need this approach because variables passed with -v expand escape sequences. So, if either of the variables you pass contain a backslash, for example foo\tbar, then the \t in the middle will be expanded and treated as a literal tab character.

See http://cfajohnson.com/shell/cus-faq-2.html#Q24 and/or https://stackoverflow.com/questions/19075671/how-do-i-use-shell-variables-in-an-awk-script for more information.

0
4

The only modification I would see is necessary is that you have a print statement in the END section, but missing one for non-matching lines. What instead you should do is

awk -F';' -v OFS=';' -v acc='bla' -v newpw='etewtw' '$1 == acc {$2=newpw}1' file.csv

i.e. simply replace field Nr. 2 with the new password, and in general print the (possibly modified) line (the shorthand notation for that is the 1). With this, I also succeeded in changing the record on the last line.

Also, it would seem wise to make your condition an exact match instead of a regex, you never know if one account name may contain a string that matches another account name (as in admin and webadmin, e.g.).

5
  • 3
    That will fail if/when either field contains a backslash since variables initialized with -v expand escape sequences. You need to set shell variables and use ENVIRON[] or pass the strings as command line arguments before the input file to have them treated exactly as-is.
    – Ed Morton
    Commented Apr 9, 2020 at 13:09
  • 2
    @EdMorton that is true. While I don't think the first field is likely to do so, the second field may very well (or, indeed, should) contain special characters that may be mis-interpreted. I see you already posted a robust answer, so I will refrain from editing mine.
    – AdminBee
    Commented Apr 9, 2020 at 13:21
  • 2
    Yeah, I might've not bothered commenting if it wasn't for the password field. You have a second issue I just noticed though - you're using double quotes instead of single around your variables and so asking the shell to interpret them so newpw="foo$PWD", for example, wouldn't behave as you want. Always use single quotes around strings and scripts unless you NEED double quotes instead (e.g. to let variables expand) and then use double quotes unless you NEED no quotes (e.g. for globbing/file name expansion).
    – Ed Morton
    Commented Apr 9, 2020 at 13:33
  • 1
    You are right. I copy-and-pasted that part from the OPs approach, and didn't check that thoroughly enough. I need to be more careful there, thank you for pointing it out. I will edit the answer.
    – AdminBee
    Commented Apr 9, 2020 at 13:35
  • 1
    @toogley I would recommend accepting Ed Morton's answer instead of mine because of the variable expansion issues discussed in the comments here. The accepted answer should be as little error-prone as possible for use by the "unsuspecting" reader who may have stumbled across it while googling.
    – AdminBee
    Commented Apr 9, 2020 at 14:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .