bash

I want my script to write $ as it is in the file


This is a part of my script

sudo bash -c "printf '%s\n' \
'user-test:' \
'  hash: \"$HASHED_PASSWORD\"' \
'  reserved: true' \
'  backend_roles:' \
'    - \"admin\"' \
'  description: \"admin user\"' >> /etc/opensearch/opensearch security/internal_users.yml"

‍‍$HASHED_PASSWORD‍‍‍ is generated using usr/share/opensearch/plugins/opensearch-security/tools/hash.sh

It generates it in this format $2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK

I want this exact value to be written in the /etc/opensearch/opensearch security/internal_users.yml file but it is unable to do so, as $2, $12, $M is treated as a variable.

This is how it actually writes.

user-test:
  hash: "y2/PO6SqvPjuXzJaCK"
  reserved: true
  backend_roles:
    - "admin"
  description: "admin user"

Solution

  • This is all about quoting.

    I made a local file to test it.

    $: cat tst
    #! /usr/bin/env bash
    set -x
    bash -c "printf '%s\n' '
    user-test:
      hash: \"${1:-${HASHED_PASSWORD:?Password Not Set}}\" # arg 1st, env 2nd, else bail
      reserved: true
      backend_roles:
        - \"admin\"
      description: \"admin user\"
    '"
    

    If the password data isn't exported in the environment OR passed in, it fails.

    $: ./tst
    ./tst: line 3: HASHED_PASSWORD: Password Not Set
    

    Pass it in -

    $: ./tst "$tmp"
    + bash -c 'printf '\''%s\n'\'' '\''
    user-test:
      hash: "$2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK" # arg 1st, env 2nd, else bail
      reserved: true
      backend_roles:
        - "admin"
      description: "admin user"
    '\'''
    
    user-test:
      hash: "$2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK" # arg 1st, env 2nd, else bail
      reserved: true
      backend_roles:
        - "admin"
      description: "admin user"
    

    (Yes, I added an explanatory comment that ends up in the yaml, but it's also a yaml comment.)

    You can set the value inline -

    $: HASHED_PASSWORD='$2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK' ./tst
    + bash -c 'printf '\''%s\n'\'' '\''
    user-test:
      hash: "$2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK" # arg 1st, env 2nd, else bail
      reserved: true
      backend_roles:
        - "admin"
      description: "admin user"
    '\'''
    
    user-test:
      hash: "$2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK" # arg 1st, env 2nd, else bail
      reserved: true
      backend_roles:
        - "admin"
      description: "admin user"
    

    Or export it -

    $: export HASHED_PASSWORD='$2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK'
    $: ./tst
    + bash -c 'printf '\''%s\n'\'' '\''
    user-test:
      hash: "$2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK" # arg 1st, env 2nd, else bail
      reserved: true
      backend_roles:
        - "admin"
      description: "admin user"
    '\'''
    
    user-test:
      hash: "$2y$12$M44wSxuwYbYRUqRKf1IUAuY5jvBlh4tu5XVx7/PO6SqvPjuXzJaCK" # arg 1st, env 2nd, else bail
      reserved: true
      backend_roles:
        - "admin"
      description: "admin user"
    

    I put the whole yaml string in one outer set of single-quotes with your newlines embedded to simplify a bit. The command using it is in double-quotes so that the single-quotes are just data in the string, and it's double-quote interpolated, so the var value goes in as data and doesn't get re-interpolated. All that gets passed to the bash -c, so it works.

    YMMV.