5

I read an array from another script. This array needs to put " " around all array members since some members are empty.

in_file=./data
vector=($(./readdata.sh 0 $in_file))
for index in ${!vector[@]}
do
    echo ${vector[index]}
done

The problem is that I have quotations around each output line and I want to get rid of them.

"red"
"blue"
"green"
""
"white"
"black"

must change to:

red
blue
green

white
black

I look for a method which does not use awk, tr, sed or any other pipeline based way. I just want to solve with using native ways such as using parenthesis, different punctuations, ....

ar2015
  • 212
  • This might help - http://stackoverflow.com/questions/9733338/shell-script-remove-first-and-last-quote-from-a-variable – rahul Apr 02 '15 at 06:27
  • @rahul i saw it. it has used sed – ar2015 Apr 02 '15 at 06:28
  • 1
    look for the accepted answer. They have used the native prefix/sufix feature without sed. sed is presented as an alternative. But the accepted answer does not use sed. – rahul Apr 02 '15 at 06:29
  • @ar2015 The accepted answer there starts with a version without sed. – Anthon Apr 02 '15 at 06:30
  • The parens are a pipeline - but much slower because they imply the output must be delayed and passed at once. A pipe is better. Anyway, since you're essentially just doing similar anyway, set -f;IFS=$'\n';eval "printf '%s\n' "$(./readdata,sh 0 "$in_file) - but that assumes that the "" double-quoted strings do not contain any characters which might be interpreted within double-quotes. – mikeserv Apr 02 '15 at 06:36
  • @rahul the accepted answer only removes prefix or suffix but not both. unless you write 3 lines. That is worse than using pipeline. – ar2015 Apr 02 '15 at 06:44
  • @mikeserv The only reason for asking question was finding an inline way with the minimum change in the code. However thanks for your solution. – ar2015 Apr 02 '15 at 06:47
  • @Anthon That accepted solution uses 3 lines. I am looking for a very simple way. an inline way with maximum readability. I have that code repeated many times in my code. it is very hard to change for all files and using as a template for the next times. My problem is not to obtain the result but how to do it. – ar2015 Apr 02 '15 at 06:50
  • 1
    @mikeserv some array members are empty strings. Then they vanish this way. Actually I am putting " " around members for this purpose. avoid putting it around members in the original script is much easier. – ar2015 Apr 02 '15 at 06:51
  • @ar2015 - (my recommendation) is really not a very good one. A good way to do it with minimal change could be ...vector=($(...| sed 's/"//;s/"$//'))... To only affect not-null members do instead: ...sed 's/"\(..*\)"/\1/'... – mikeserv Apr 02 '15 at 07:00
  • @ar2015 if readability counts then an one liner often (but not always) is the wrong solution. But what is certainly seems wrong to me is that you have to update your code in multiple locations, you should your reused code in a function and only update that one location. How else are you going to do sensible unit testing before using your scripts? – Anthon Apr 02 '15 at 07:03
  • @Anthon I am writing a C++ code and bash files are to generate automatic codes. I am in hurry of finishing my C++ code. So bash is not my main purpose. Still I have convincing reason to keep my code this way. first of all each part do different job and cannot be encapsulated. second is that this code is not large enough to be put in a different bash file. – ar2015 Apr 02 '15 at 07:10

4 Answers4

4

This might work:

in_file=./data
vector=($(./readdata.sh 0 $in_file))
for index in ${!vector[@]}
do
    echo ${vector[index]//\"/}
done

Ref: http://www.tldp.org/LDP/abs/html/refcards.html#AEN22828

FloHimself
  • 11,492
  • I was looking for a clean way. A readable way. This is the best ever solution. – ar2015 Apr 02 '15 at 06:52
  • 3
    You should enclose the entire expression in double quotes (echo "${vector[index]//\"/}") in case the value has leading spaces, trailing spaces, or embedded strings of multiple spaces.  Note that this will remove embedded (internal) quotes as well as the ones at the beginning and end.  (And the ; at the end of the command line is unnecessary.) – Scott - Слава Україні Apr 02 '15 at 07:32
  • @Scott in my case it does not happen because the scrip puts quotations. but thanks for mentioning. – ar2015 Apr 02 '15 at 07:36
  • I don't understand which part of my comment you're responding to, or how your comment makes sense as a response to mine. – Scott - Слава Україні Apr 02 '15 at 07:38
  • @Scott the ; at the end of the command was introduced by pasting the snipped into my terminal. It is not in the OP's snippet. I'll remove it from the snippet. There are several other things that could be optimized, but this wasn't the requirement in this question, so I keep it the way it was posted by the OP. – FloHimself Apr 02 '15 at 07:48
3

You'll make your program simpler and more robust if readdata.sh produces newline-delimited data, with no extra quotes.

In your current program, the output of readdata.sh is split at whitespace (so e.g. "a b" results in two array elements "a and b") and each resulting word is interpreted as a wildcard pattern (so e.g. "a * b" results in "a, then the file names in the current directory, and finally b"). See Why does my shell script choke on whitespace or other special characters? for more details.

Bash provides a very simple way of reading newline-delimited data: the mapfile builtin. Since bash executes the right-hand side of a pipeline in a subshell, you can't just write ./readdata.sh 0 "$in_file" | mapfile -t vector, you have to put the use of the variable in a command block, or you can use process substitution:

in_file=./data
mapfile -t vector < <(./readdata.sh 0 "$in_file")
for index in "${!vector[@]}"
do
  echo "$index: ${vector[index]}"
done

If you aren't using the indices, only the elements, a simpler way to iterate over the array is

for element in "${vector[@]}"
do
  echo "$element"
done
2

As you prefer a one liner,

vector=("${vector[@]//\"/}")

Remember that text substitutions could work for the array as a whole.

xae
  • 2,021
1

If you want to avoid a "hacky" search-and-replace solution, using eval seems to do the job:

vector=( $(eval echo ${vector[@]}) )

This causes the shell to interpret them as quoted arguments to echo, which removes the quotes. Of course, the line above as it is has still problems with white space, but the key point here is the idea of using eval.