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
Jivan
  • 899
  • 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) https://stackoverflow.com/questions/13799789/expansion-of-variables-inside-single-quotes-in-a-command-in-bash – Jivan Feb 26 '20 at 17:07

3 Answers3

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

steeldriver
  • 81,074
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
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.

  • 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. – Paul_Pedant Feb 26 '20 at 20:36