2

Like HERE I have a file.csv with numbers in quotes:

"0.2"
"0.3339"
"0.111111"

To round the number (3 decimals) this solutions works great:

printf "%.03f\n" $(sed 's/\"//g' file.csv)

But now I want to store sed 's/\"//g' file.csv as a variable

var_sed=$(sed 's/\"//g' file.csv);
printf "%.03f\n" ${var_sed}

Doesn't work. The output is

zsh: bad math expression: operator expected at `0.3339\n0.1...'
0.000

So the problem is that var_sed passes \n to printf "%.03f\n".

The only solution I know is:

var_sed=$(sed 's/\"//g' file.csv);
printf "%.03f\n" $(echo ${var_sed})
  1. Maybe there is a cleaner way.
  2. Also I want to put printf "%.03f\n" in a function like this:
printf_function () { printf "%.03f\n" $1;}

But this is not working:

printf_function () { printf "%.03f\n" $1;};
var_sed=$(sed 's/\"//g' file.csv);
printf_function ${var_sed}

also printf_function $(echo ${var_sed}) is not working.

____________________________________________________________

Why I try to save the file in a variable? ____________________________________________________________

The truth is that I actually want to put the sed command in a function. I'm sorry I put this as a variable to (try to) simplify the problem.

My script is

sed_01 () { sed 's/\"$// ; s/^\"// ; s/something_else//g' $1;};
printf_03 () { printf "%.03f\n" $1;};
printf_03 "$(sed_01 file.csv)"

as mentioned in the comments below, it works in bash.

output:

"0.200"
"0.334"
"0.111"

but in zsh the output: is:

printf_03: bad math expression: operator expected at `0.3339\n0.1...'
0.000
3
  • I'm thinking the reason why printf "%.03f\n" $(echo ${var_sed}) works and not printf "%.03f\n" ${var_sed} is because the command substitution $(the comand) removes all trailing new lines Commented Feb 26, 2023 at 4:08
  • 1
    Why would you want to save the file in a variable? Strip the quotes inside your function, pass an option and a filename as arguments. If the option is not present, quotes are not removed.
    – Freddy
    Commented Feb 26, 2023 at 4:27
  • See also the second half of my answer to the question you link to, which is specifically about CSV input with quoted fields.
    – Kusalananda
    Commented Feb 26, 2023 at 6:20

1 Answer 1

3
printf "%.03f\n" $(sed 's/\"//g' file.csv)

var_sed=$(sed 's/\"//g' file.csv);
printf "%.03f\n" ${var_sed}

This relies on word splitting to give the numbers to printf as separate arguments. The thing is, that zsh has dropped automatic word splitting as a silly remnant of the past, and doesn't do it for variable expansions. But it does do it for command substitutions, for whatever reason.

There's also no splitting when assigning from the command substitution to a regular scalar variable, so the newlines in the output of sed get saved in the variable.

That's why your code works with the command substitution, but not with the intermediate variable.

If you do need to load the values in a variable first (and you don't for just printing), consider storing them in an array (zsh):

lines=("${(f)$(sed 's/\"//g' file.csv)}")
printf "%.03f\n" $lines

in Bash, you'd use readarray/mapfile:

mapfile -t lines < <(sed 's/\"//g' file.csv)
printf "%.03f\n" "${lines[@]}"

Or if you want something that mostly works in either, you'll have to rely on word splitting again:

lines=( $(sed 's/\"//g' file.csv) )
printf "%.03f\n" "${lines[@]}"

Note the parentheses, they make that an array assignment, so word splitting again happens. ("mostly" including the usual caveats of a modified IFS affecting the behaviour, and filename globbing also happening.)


Then again, something like this might be easier done with just AWK:

$ awk '{gsub(/"/, ""); printf "%.03f\n", $1}' file.csv
0.200
0.334
0.111
2
  • Most shells that don't have that misfeature of the Bourne shell of doing split+glob upon parameter expansion still do some form of splitting upon command substitution and not globbing (rc, es, fish...). csh still does splitting upon quoted command substitutions (though line based in that case). Main reason is because that's generally what you want. Commented Mar 3, 2023 at 6:42
  • Note that in zsh, to split on newline, you use the f parameter expansion flag (short for ps[\n]). non_empty_lines=( ${(f)"$(cmd)"} ). Here in case some of the lines have things like 1 + 1. Note that because printf in zsh (like in ksh) does arithmetic evaluation in numeric arguments, that would make this a command injection vulnerability if the input was not sanitised. Commented Mar 3, 2023 at 6:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .