Ram Kashyap
Ram Kashyap

Reputation: 67

Assign lines with spaces in array from a text file in shell script

I am very new to shell script and trying my best to get hands on this language. I am trying to assign each line available in a text file to an array

For example, I have a text file as below. enter image description here

I want to assign each line to an array as below.

Expected Output:

Array[0]="delectus aut autem"
Array[1]="quis ut nam facilis et officia qui"
Array[2]="fugiat veniam minus"

But, on executing the below code, We are observing that each element in array is assigned based on the space in each line

Shell Script:

#!/bin/sh

cmd=$(curl  https://jsonplaceholder.typicode.com/todos)


echo $cmd > Data.json

echo $(jq '.[] | .title' Data.json) > title.txt

echo "text file creation done!"

filename=title.txt
declare -a myArray
myArray=(`cat "$filename"`)

printf '%s\n' "${myArray[@]}"

for (( i = 0 ; i < 20 ; i++))
do
  echo "Element [$i]: ${myArray[$i]}"
done

Current Output:

enter image description here

Can someone throw some light on this?

Upvotes: 2

Views: 45

Answers (2)

Shelton Liu
Shelton Liu

Reputation: 601

What you can try to use a while loop to read each line into an array

#!/bin/sh

cmd=$(curl -s https://jsonplaceholder.typicode.com/todos)
echo "$cmd" > Data.json

jq -r '.[] | .title' Data.json > title.txt


myArray=()

# Read each line
while IFS= read -r line; do
    myArray+=("$line")
done < title.txt

for (( i = 0 ; i < 20 ; i++ )); do
  echo "Array[$i]=\"${myArray[$i]}\""
done

Upvotes: 0

David C. Rankin
David C. Rankin

Reputation: 84609

Your biggest issue is using the POSIX shell interpreter line #!/bin/sh. POSIX shell does not support arrays. You need to use an advanced shell like bash, which provides for both indexed, declare -a arrays and associative, declare -A arrays. Given you use declare -a it appears you mean to use bash. So change the interpreter line to #!/bin/bash and you will have arrays available.

You are further in luck, bash provides mapfile (synonymous with readarray) builtin commands that will read a file, line-by-line, into elements of an indexed array. You can use either mapfile or readarray, they are the same command, just aliases for each other.

When you want to loop over the array, use the parameter expansion ${#myArray[@]} to return the number of elements in the array rather then hardcoding 20 or some other number.

Don't use cat ... unnecessarily to pipe information around. That is called a UUOc (Unnecessary Use of cat, also stated as Useless Use of cat). See Useless Use of Cat. Instead use > and < to redirect to/from files.

Always paste your script into ShellCheck to find any issue (good idea before posting here too)

Putting it altogether, you could modify your script as follows. This isn't the only way to do it, but a reasonable one:

#!/bin/bash

## assign filenames from 1st two command-line arguments
#  use Data.json and title.txt by default if no arguments given
websrc="${1:-Data.json}"
txtfile="${2:-title.txt}"

## use redirection to save the curl data to Data.json
curl https://jsonplaceholder.typicode.com/todos > "$websrc"

## use redirection to save jq output to title.txt
jq '.[] | .title' "$websrc" > "$txtfile"

## optional output, but make information useful
#  check "%txtfile" exists and is non-empty
if [ -s "$txtfile" ]; then
  printf "text file creation done!\n"
else  # otherwise handle the error and exit with error code 1
  printf "error: failed to create txtfile '%s'\n" "$txtfile" >&2
  exit 1
fi

## declare indexed array
declare -a myArray

## read the $txtfile into array 
mapfile -t myArray < "$txtfile"

## no need to duplicate output
# printf '%s\n' "${myArray[@]}"

## use array to determine number of elements (200 in test)
for (( i = 0 ; i < ${#myArray[@]}; i++))
do
  printf "Element [%d]: %s\n" "$i" "${myArray[$i]}"
done

Note: See Why is printf better than echo? to understand why I changed your echo ... use to printf ...

Example Use/Output

Since you provided the URL, the output of the script is:

$ bash curl-json-mapfile.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 24311    0 24311    0     0   136k      0 --:--:-- --:--:-- --:--:--  137k
text file creation done!
Element [0]: "delectus aut autem"
Element [1]: "quis ut nam facilis et officia qui"
Element [2]: "fugiat veniam minus"
Element [3]: "et porro tempora"
Element [4]: "laboriosam mollitia et enim quasi adipisci quia provident illum"
Element [5]: "qui ullam ratione quibusdam voluptatem quia omnis"
Element [6]: "illo expedita consequatur quia in"
Element [7]: "quo adipisci enim quam ut ab"
Element [8]: "molestiae perspiciatis ipsa"
Element [9]: "illo est ratione doloremque quia maiores aut"
Element [10]: "vero rerum temporibus dolor"
...
Element [195]: "consequuntur aut ut fugit similique"
Element [196]: "dignissimos quo nobis earum saepe"
Element [197]: "quis eius est sint explicabo"
Element [198]: "numquam repellendus a magnam"
Element [199]: "ipsam aperiam voluptates qui"

All 200 elements successfully stored in the array, and much easier than looping with read -r (which is another valid option)

Look things over and let me know if you have questions.

Upvotes: 1

Related Questions