0

I have the following script:

#!/bin/bash
cat $1.txt | awk "{cmd=\"pwgen 10 1\"; cmd | getline pass; print substr($0, 1, length($0) -1) \">> ~/dresses/whole-dresses-shape/$1-\" pass \".jpg\"; close(cmd);}" | bash

Inside the awk "{}" command, the $0 variable is supposed to refer to the line being currently read from cat $1.txt. However, when used from inside a script file, $0 takes the value of the name of the file being executed.

How can I make sure that the substr() function takes the output from cat in this example?

Desired workflow:

  • user enters ./myscript.sh hello
  • $1 takes hello value
  • each line read from cat is assigned to what is currently displayed as $0
2
  • I tried exactly that, and the problem then becomes that it is $1 which is not right anymore. It becomes a literal $1, instead of recalling the variable. Any way to escape this?
    – Jivan
    Feb 26 '20 at 16:57
  • found a way to "escape" variables within single-quotes (well, sort-of) stackoverflow.com/questions/13799789/…
    – Jivan
    Feb 26 '20 at 17:07
0

Do it like this:

variable="but put shell variable in double quotes"

echo 'Put most of the awk script in single quotes so that $s are not expanded, '"$variable"', then continue in single quotes.'

However, please, read @Paul_Pedant's comment, about injecting code in to a program.

2
  • Not sure why someone downvoted, but it's the solution I ended up going with.
    – Jivan
    Feb 26 '20 at 18:08
  • 1
    It's not good because it injects shell text directly into the awk program itself. So a bad content for $variable can throw an awk syntax error, or even put in spurious valid awk commands. If you pass variables in using -v, they will be constrained to be plain variables, not arbitrarily complex in-line code. Feb 26 '20 at 20:36
3

Here are a couple of ways to do it more cleanly (the use of printf in place of print + string concatenation is a matter of personal preference - I find that it is more readable in this case).

  1. export the value of $1 and use the awk ENVIRON array ex.

    #!/bin/bash
    
    export var="$1"
    
    cat "$1.txt" | awk '
      BEGIN{cmd="pwgen 10 1"} 
      {
        cmd | getline pass; 
        printf "%s >> ~/dresses/whole-dresses-shape/%s-%s.jpg\n", substr($0, 1, length($0) -1), ENVIRON["var"], pass;
        close(cmd);
      }
    '
    
  2. use the command line -v option to pass the value of $1 ex.

    #!/bin/bash
    
    cat "$1.txt" | awk -v var="$1" '
      BEGIN{cmd="pwgen 10 1"} 
      {
        cmd | getline pass; 
        printf "%s >> ~/dresses/whole-dresses-shape/%s-%s.jpg\n", substr($0, 1, length($0) -1), var, pass;
        close(cmd);
      }
    '
    

Both of these replace the outer "soft" quotes with 'hard' quotes. A side-benefit is that it frees up a layer of quoting in case you wish to protect the output strings from word-splitting (probably good practice - since you are apparently piping the result to a shell), ex.

printf "\"%s\" >> ~\"/dresses/whole-dresses-shape/%s-%s.jpg\"\n"

For a discussion of -v versus ENVIRON please see

0
0

I would have used single quotes for the awk. Not tested

#!/bin/bash
cat $1.txt | awk '{cmd="pwgen 10 1"; cmd | getline pass; print substr($0, 1, length($0) -1) ">> ~/dresses/whole-dresses-shape/'$1'-" pass ".jpg"; close(cmd);}' | bash
2
  • The embedded single-quotes around $1 break it: they get $1 from the shell, not from the awk input. But then that whole print is longing to be a printf with a proper format and two args. Feb 26 '20 at 20:42
  • 1
    No it is the trick. He wants $1 as shell argument. Feb 26 '20 at 23:07

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.