Home > other >  Split string and extract variables with shell script
Split string and extract variables with shell script

Time:12-03

Question

Given this single-line string:

PG_USER=postgres PG_PORT=1234 PG_PASS=icontain=and*symbols

What would be the right way to assign each value to its designated variable so that I can use it afterward?


Context

I'm parsing the context of a k8s secret within a CronJob so that I can periodically call a Stored Procedure in our Postgres database.

To do so, I plan on using:

PG_OUTPUT_VALUE=$(PGPASSWORD=$PG_PASSWD psql -qtAX -h $PG_HOST -p $PG_PORT -U $PG_USER -d $PG_DATABASE -c $PG_TR_CLEANUP_QUERY)

echo $PG_OUTPUT_VALUE

The actual entire helm chart I'm currently trying to fix looks like this:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: {{ template "fullname" $ }}-tr-cleanup-cronjob
spec:
  concurrencyPolicy: Forbid
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          volumes:
          - name: postgres
            secret:
              secretName: {{ template "fullname" $ }}-postgres
          containers:
          - name: {{ template "fullname" $ }}-tr-cleanup-pod
            image: postgres:12-alpine
            imagePullPolicy: Always
            env:
              - name: PG_PROPS
                valueFrom:
                  secretKeyRef:
                    name: {{ template "fullname" $ }}-postgres
                    key: postgres.properties
            command:
              - /bin/sh
              - -c
              - echo "props:" && echo $PG_PROPS && PG_USER=$(grep "^PG_USER=" | cut -d"=" -f2-) && echo $PG_USER && PG_TR_CLEANUP_QUERY="SELECT something FROM public.somewhere;" && echo $PG_TR_CLEANUP_QUERY && PG_OUTPUT_VALUE=$(PGPASSWORD=$PG_PASSWD psql -qtAX -h $PG_HOST -p $PG_PORT -U $PG_USER -d $PG_DATABASE -c $PG_TR_CLEANUP_QUERY) && echo PG_OUTPUT_VALUE
            volumeMounts:
              - name: postgres
                mountPath: /etc/secrets/postgres

Current approach

As you can see, I'm currently using:

PG_USER=$(grep "^PG_USER=" | cut -d"=" -f2-)

That is because I initially thought the secret would be output on multiple lines, but it turns out that I was wrong. The echo $PG_USER displays an empty string.

CodePudding user response:

This function can be reused to assign each variable individually:

extract() {
  echo "$INPUT" | grep -o "$1=.*" | cut -d" " -f1 | cut -d"=" -f2- ;
}

And to use it:

PG_USER=$(extract PG_USER)
PG_PORT=$(extract PG_PORT)
PG_PASS=$(extract PG_PASS)

Another potential solution, with a security concern, is to simply use:

eval "$INPUT"

It should only be used if you have validated the input.

CodePudding user response:

The bash declare command is appropriate here, and is safer than eval.

Suppose the input contains something potentially malicious

line='PG_USER=postgres PG_PORT=1234 PG_PASS=icontain=and*symbols`ls`'

I'm assuming none of the values contain whitespace. Let's split that string

read -ra assignments <<< "$line"

Now, declare each one

for assignment in "${assignments[@]}"; do declare "$assignment"; done

Everywhere we examine the input, we maintain double quotes.

Let's see what we ended up with:

$ declare -p PG_USER PG_PORT PG_PASS
declare -- PG_USER="postgres"
declare -- PG_PORT="1234"
declare -- PG_PASS="icontain=and*symbols\`ls\`"
  • Related