0

Solution

As @Barmar points out, a carriage return was the problem. But couldn't find out where it was coming from, as it wasn't .gitconfig nor Visual Studio Code.

jq was pulling in the /r/n per this issue on GitHub. It doesn't look like their latest release includes the fix yet (--binary on windows).

Piping the output to tr during the array creation solved it.

environmentsNames=($(jq -r '.environments[].name' <<< "${configJSON}" | tr -d '\r')) # Returns an array of environment names

Orginal Problem

I'm putting together an open-source bash repo to make SSH/SSH-Agent/SFTP/Rsync connections and authentication easier/faster.

The user selects an environment to connect to, which is read from a json config file by jq. They see a message indicating what was selected, but this message is cut off per the below.

I've tried adding additional spaces, echo vs printf, changing the line's length, different terminals, removing the quotes and adding additional text, and more. In each case, this line is cut off to a specific number of characters that seems to be like 35-37.

It's not affecting functionality, but it's annoying and I'm curious to learn what I'm doing wrong. No idea why, but I've done some reading - is the shell consuming characters due to the length of my variables or something?

Thanks!

Here is what I am seeing:

Enter the number of the environment to work from. Use staging by default. 0
'ou selected '0' which represents 'stg

Here is what I expect:

Enter the number of the environment to work from. Use staging by default. 0
You selected '0' which represents 'stg'

Here is the code:

# Select environment to work from to set environment variables
envNumbers=() # Declare empty array
for i in "${!environmentsNames[@]}"; do 
    envNumbers+=($i) # Create array of environment numbers so it can be checked against later
    printf "%s\t%s\n" "$i" "${environmentsNames[$i]}"  # List environment names from environments.json file
done
chosenEnv=""
for i in 1 2 3; do # Three attempts to select the right environment
    printf '\n'
    read -p "Enter the number of the environment to work from. Use staging by default. " chosenEnv
    if [[ ! "${envNumbers[@]}" =~ "$chosenEnv" ]]; then # Checks to ensure environment is valid
        printf "Your entry $chosenEnv is not a valid environment number. \n"
    else
        echo "You selected '$chosenEnv' which represents '${environmentsNames[$chosenEnv]}'"
        break # Leaves the loop if environment is valid
    fi
    if [[ $i == 3 ]]; then # On last try, exit the script
        printf "You did not select a valid environment. Goodbye. \n"
        sleep 5
        exit 1 # Leaves the script
    fi
    printf "Try again. \n"
done

Edits

Here is where the environments variables is populated:

#!/bin/bash
configJSON="$(jq -r '.' "$DIR/config.json")" # Returns all environment data
defaultIdentity=$(jq -r '.defaultIdentity' <<< "${configJSON}")
sshAgentTime=$(jq -r '.sshAgentTime' <<< "${configJSON}")
environmentsNames=($(jq -r '.environments[] | .name' <<< "${configJSON}")) # Returns an array of environment names

Here is the template config json:

{
    "defaultIdentity":"path/to/rsa/private/key",
    "sshAgentTime":"4h", 
    "environments":
    [
        {
            "name":"environment1",
            "address":"12.345.67.890",
            "username":"myusername",
            "port":"99999",
            "path":"path/to/target/directory",
            "identity":"path/to/rsa/private/key",
            "siteurl":"example1.com"
        }, 
        {
            "name":"environment2",
            "address":"12.345.67.899",
            "username":"myusername",
            "port":"99999",
            "path":"path/to/target/directory",
            "identity":"path/to/rsa/private/key",
            "siteurl":"example2.com"
        }
    ]
}

Once environment is selected, this runs to pull the selected environments credentials:

#!/bin/bash
if [ $chosenEnv ]; then
    envName=$(jq -r --argjson chosenEnv $chosenEnv '.environments[$chosenEnv].name' <<< "${configJSON}")
    envDomain=$(jq -r --argjson chosenEnv $chosenEnv '.environments[$chosenEnv].siteurl' <<< "${configJSON}")
    envUsername=$(jq -r --argjson chosenEnv $chosenEnv '.environments[$chosenEnv].username' <<< "${configJSON}")
    envAddress=$(jq -r --argjson chosenEnv $chosenEnv '.environments[$chosenEnv].address' <<< "${configJSON}")
    envPort=$(jq -r --argjson chosenEnv $chosenEnv '.environments[$chosenEnv].port' <<< "${configJSON}")
    envIdentity=$(jq -r --argjson chosenEnv $chosenEnv '.environments[$chosenEnv].identity' <<< "${configJSON}")
    envLogin="$envUsername@$envAddress"
    envPath=$(jq -r --argjson chosenEnv $chosenEnv '.environments[$chosenEnv].path' <<< "${configJSON}")
else 
    printf "Environment not selected or defined."
    sleep 3
    exit 1
fi
jmtornetta
  • 85
  • 6
  • 2
    You have a carriage return somewhere. – Barmar Jul 28 '21 at 17:05
  • `${environmentsNames[$chosenEnv]}` ends with carriage return. How are you creating that array? – Barmar Jul 28 '21 at 17:07
  • How did `environmentsNames` get created? Most likely, some or all of the values in that array end with a `\r`. – chepner Jul 28 '21 at 17:07
  • 1
    If you read it from a file, use `dos2unix` to fix the file. – Barmar Jul 28 '21 at 17:07
  • And why are you using `=~`? You can check if `chosenEnv` is a defined index with `[[ -v envNumbers[$chosenEnv] ]]`, but I recommend having a look at the `select` command to build, display, and interact with a text menu. – chepner Jul 28 '21 at 17:09
  • @chepner via a variable in the vars folder that pulls from the config.json file via jq. https://github.com/jmtornetta/easy-ssh/blob/main/vars/configToVars – jmtornetta Jul 28 '21 at 17:22
  • Please put that in the question; the comment is virtually unreadable. – chepner Jul 28 '21 at 17:24
  • Ughh yes thank you. Doing now! – jmtornetta Jul 28 '21 at 17:25
  • @Barmar added the jq that populates the array from json file up there. I will try dos2unix. ty! – jmtornetta Jul 28 '21 at 17:32
  • @chepner - no good reason, it's just want I found online but yes your solution is better and shorter ty. I will look into select, haven't used it. – jmtornetta Jul 28 '21 at 17:32
  • I don't think running `jq -r . config.json` does anything useful. – chepner Jul 28 '21 at 17:35
  • @chepner I read somewhere that storing in a variable might improve performance for a lot of subsequent calls but I really have no idea tbh. Definitely not right now, as scripts are small. – jmtornetta Jul 28 '21 at 17:38
  • FYI -- it's not ideal, but you can use `${var//$'\r'/}` to refer to a version of `$var` with the CRs removed, or `${var%$'\r'}` to do the same only for one at the very end. – Charles Duffy Jul 28 '21 at 19:55
  • ty @CharlesDuffy , I've been incorporating into other scripts! – jmtornetta Aug 02 '21 at 21:30
  • BTW, calling jq once per variable is definitely a lot slower than having just one jq call extract _all_ your variables. – Charles Duffy Aug 02 '21 at 23:51
  • On _how_ to extract multiple variables with just one jq call, the answer by Sid at https://stackoverflow.com/a/51690860/14122 is a very good one. Note the use of `@sh` when generating content to be `eval`ed -- that's critical for security; don't use any answer where data is `eval`ed by bash unless it was quoted with `@sh` on the jq end. (The other safe approach is to use `jq -j` to emit data with `"\u0000"`s -- NULs -- between elements, and `IFS= read -r -d ''` on the bash side to consume that data). – Charles Duffy Aug 02 '21 at 23:53

0 Answers0