1

Hello and thanks for the help.

I'm having difficulty escaping the single quote character (') in a Windows Batch script. I've tried ^', \', ''', '\'', and so on. Online searches haven't solved it either. I'm stumped.

This script uses cgywin64 gawk to format a list of folders:

@echo off
du -sk ./*  | sort -nr | head -40 | gawk '{printf("%%^'14d kB %%s %%s %%s %%s %%s %%s %%s %%s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9)}'

When run, it gives me the following errors:

gawk: cmd. line:1: {printf("%^14d
gawk: cmd. line:1:         ^ unterminated string
gawk: cmd. line:1: {printf("%^14d
gawk: cmd. line:1:         ^ syntax error

Without doing any character escaping, the gawk command would look something like:

gawk '{printf("%'14d kB %s %s %s %s %s %s %s %s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9)}'

The format string at the beginning (%'14d) tells gawk to use commas when printing the integer ("123456789" prints as "123,456,789"). This string has a single quote ('), which I can't figure out how to pass into the batch script without errors.

So how do I pass a single quote (') into a batch script?

Any help appreciated.

Compo
  • 36,585
  • 5
  • 27
  • 39
wonder
  • 33
  • 4
  • 1
    Try the command line: `du -sk ./* | sort -nr | head -40 | gawk "{printf(\x22%%'14d kB %%s %%s %%s %%s %%s %%s %%s %%s\n\x22, $1, $2, $3, $4, $5, $6, $7, $8, $9)}"`. I don't have __gawk__ installed. For details why using `"` around argument string and referencing `"` inside the string with `\x22` see answer on [gawk command in CMD with && operator not working](https://stackoverflow.com/a/38174270/3074564). – Mofi Mar 16 '18 at 07:23
  • 1
    The single quotes shouldn't be an issue, your double quotes and percent characters however are. You may find the official [user guide](https://www.gnu.org/software/gawk/manual/html_node/DOS-Quoting.html) advice about quoting worth a read. – Compo Mar 16 '18 at 08:49
  • 1
    [Single quotes have no special meaning in Windows batch](https://stackoverflow.com/q/24173825/995714), so `'{printf("%%^'14d kB %%s %%s %%s %%s %%s %%s %%s %%s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9)}'` is not a single parameter like you expected. You must always use `"`. Or just change to powershell where you can use single quotes – phuclv Mar 16 '18 at 09:06
  • Mofi, thank you for your comments. Unfortunately, adding \x22 failed with a syntax error at \x22. But following Compos answer below worked. Again, thank you everyone for the help! – wonder Mar 16 '18 at 16:39

1 Answers1

1

The following advice is untested, and is currently based on my limited understanding.

gAwk uses single quotes for quoting in POSIX-compliant, Bourne-style shells of which cmd.exe isn't one.

With cmd.exe those single quotes used for quoting, ', need to be replaced by double quotes, ".

The single quote not used for quoting, %'14d, does not require special attention by gAwk because characters within double quotes are protected.

In cmd.exe single quotes are not problematic, so this character can remain untouched.

So in cmd.exe you need to replace the surrounding single quotes '{ and }' with double quotes "{ and }".

Then you have to escape the internal double quotes to prevent cmd.exe from closing the opening one, "{, prematurely.

In cmd.exe a double quote is usually escaped by preceding it with a backslash, \".

Your command in cmd.exe would therefore look like this:

du -sk ./*  | sort -nr | head -40 | gawk "{printf(\"%'14d kB %s %s %s %s %s %s %s %s\n\", $1, $2, $3, $4, $5, $6, $7, $8, $9)}"

The advice above is for cmd.exe; however when running from a batch file there is an additional gotcha.

All percent characters require doubling! % becomes %%.

So given all of the above, remembering that this is completely untested, your command in a batch file would be:

du -sk ./*  | sort -nr | head -40 | gawk "{printf(\"%%'14d kB %%s %%s %%s %%s %%s %%s %%s %%s\n\", $1, $2, $3, $4, $5, $6, $7, $8, $9)}"
Compo
  • 36,585
  • 5
  • 27
  • 39
  • What is unclear to me is the actual sequence `%'14d`, I'm not sure that I've ever seen that notation. I would expect that `%14d` would output/pad to 14 digits, and I have noticed both `%'d` and `%'i`, _(interchangeable digit and integer)_ used for outputting with thousands' grouping characters according to any system set locale information. My assumption therefore is that that this sequence is supposed to output a 14 digit number with locale thousands separators, _(not necessarily commas or any)_! – Compo Mar 16 '18 at 12:06
  • Compo, your observation that %'14d "is supposed to output a 14 digit number with locale thousands separators, (not necessarily commas or any)!" is also correct (as I just discovered). I now have to figure how to set cygwin local information to use commas in numbers (I'm porting a linux script where it worked with the default local info. Looks like I've got to set the cygwin local info.) Again, thanks for the help! – wonder Mar 16 '18 at 16:48
  • If the command worked without error, just without the separators in a system with no separators set, you should still mark the answer as accepted, because it effectively runs the command as intended. – Compo Mar 16 '18 at 17:02
  • Nice and detailed explanation, I appreciate. Unfortunately, there are cases that don't fit this explanation. What to do when we use this: `for /f "tokens=*" %%a in ('echo select 'TEST' from dual; ^| sqlplus -s user/password`) do echo %%a`. We definitely need to escape the single quote but I can't figure out how... Any idea? – MensSana Jan 21 '20 at 16:38
  • @menssana, that is not relevant to the original question or my answer as your issue is with using a [tag:for-loop]. If you open up a Command Prompt window and enter `for /?` you should notice within the section under `FOR /F` the `usebackq` option, so try that instead. e.g. `For /F "UseBackQ Tokens=*" %%I In (\`Echo select 'TEST' from dual; ^| SQLPlus -s user/password\`)Do Echo %%I`. – Compo Jan 21 '20 at 16:46