Home > Software design >  Line by line parsing with bash -c
Line by line parsing with bash -c

Time:11-03

In bash, I parse a file line by line and extract the first and second field, like this:

$ cat myfile
a1 1
a2 2
a3 3
a4

$ while read -r first second; do echo first is $first second is $second; done < myfile
first is a1 second is 1
first is a2 second is 2
first is a3 second is 3
first is a4 second is

Now, I need to enclose the above command in bash -c, since it will be executed through kubectl exec. It doesn't work as expected and only evaluates what it has parsed in the last line:

$ bash -c "while read -r first second; do echo first is $first second is $second; done < myfile"
first is a4 second is
first is a4 second is
first is a4 second is
first is a4 second is

What's missing here?

Thanks!

CodePudding user response:

Your parameters expands in the parent shell, change your quotes or escape the parameter expansions:

$ bash -c 'while read -r first second; do echo first is $first second is $second; done < myfile'

or

$ bash -c "while read -r first second; do echo first is \$first second is \$second; done < myfile"

Note that you should almost always wrap your parameter expansions in double quotes:

echo "$a"

instead of

echo $a

To avoid word splitting and pathname expansion.

Conside the following example, in a POSIX shell:

a="hello *"; echo $a;

vs

a="hello *"; echo "$a";

So that would make your script end up looking like:

$ bash -c 'while read -r first second; do echo first is "$first" second is "$second"; done < myfile'

CodePudding user response:

And if you do not want to deal with intricacies of shell double-qouting, first define a function where you type your code manually where also shellcheck can help you typing proper code:

f() { 
   while IFS=' ' read -r first second; do
        echo "first is $first second is $second";
   done
} 

then do:

printf "%q\n" "$(declare -f f); f"
$'f () \n{ \n    while IFS=\' \' read -r first second; do\n        echo "first is $first second is $second";\n    done\n}; f'

declare prints the function deifnition in a re-usable form, and after it we f call the function. Then you can copy the output and re-use it in shell:

bash -c $'f () \n{ \n    while IFS=\' \' read -r first second; do\n        echo "first is $first second is $second";\n    done\n}; f'

The $'...' is specific to Bash C-quoting style, so use a different quoting function than printf %q when needed.

  • Related