-1

I have the path of "file1 Nov 2018.txt" stored in variable "var". Then I use this shell variable inside the awk script to generate another script (this is a small example). The issue is the path and the filename have spaces and even I put the variable between double quotes "" and within awk I put between single quotes '' is not working either. I get the error "No such file or directory"

How to handle this path that has spaces?

The script is like this:

var="/mydrive/d/data 2018/Documents Nov/file1 Nov 2018.txt"
z=$(awk -v a="$var" 'BEGIN{str = "cat " 'a' ; print str}')
eval "$z"

I get these errors:

$ eval "$z"
cat: /mydrive/d/data: No such file or directory
cat: 2018/Documents: No such file or directory
cat: Nov/file1: No such file or directory
cat: Nov: No such file or directory
cat: 2018.txt: No such file or directory

Thanks for any help.

Ger Cas
  • 2,188
  • 2
  • 18
  • 45
  • Possible duplicate of [How do I use shell variables in an awk script?](https://stackoverflow.com/questions/19075671/how-do-i-use-shell-variables-in-an-awk-script) – oguz ismail Nov 29 '18 at 22:09
  • 2
    what do you really want? your logic looks very strange. your awk build a `cat file` string, then you `eval theStr`... why?! you know awk can load the file, also you can just `cat` without awk... – Kent Nov 29 '18 at 22:10
  • Try escaping the spaces in the file name. – TenG Nov 29 '18 at 22:12
  • @Kent Like I say this is a small example to illustrate. In my actual script awk takes the output of another script and generates an imagemagick script, but the issue is the same, the script doesnt work because the paths contains spaces. So in my original script I have a long awk logic that stores all in variable "str" and this variable contains the final script that I run with eval. I hope make sense. – Ger Cas Nov 29 '18 at 22:15
  • @TenG The issue is in my real script the path are dinamically obtained, the path are not hard coded, because of that I cannot escape each space. – Ger Cas Nov 29 '18 at 22:17
  • @oguzismail Uhhh, I don't think so. – Ger Cas Nov 29 '18 at 22:27
  • 4
    Mandatory evil `eval` comment! – karakfa Nov 29 '18 at 22:28
  • You might want to read https://mywiki.wooledge.org/BashFAQ/048 and https://mywiki.wooledge.org/BashFAQ/050 – Ed Morton Nov 30 '18 at 01:22
  • @GerCas, regarding escaping spaces, try this. Try with the hard coded path with escapes in your test scripts to see if that works. If it does, you could possible try escaping the spaces in variable `the_path` as `the_path=$( printf '%q' "${the_path1}" ) ` . – TenG Nov 30 '18 at 14:01

4 Answers4

5

The single-quote escape sequence comes in handy here. Note that 047 is the value in octal for the ASCII ' character, and awk allows you to use \nnn within a string to include any character using its octal value.

$ cat 'foo bar.txt'
a  b c
1  2 3

$ var="foo bar.txt"

$ echo "$var"
foo bar.txt

$ z=$(awk -v a="$var" 'BEGIN{print "cat \047" a "\047"}')

$ eval "$z"
a  b c
1  2 3

Maybe it's a bit nicer with printf:

$ awk -v a="$var" 'BEGIN{ printf "cat \047%s\047\n", a }'
cat 'foo bar.txt'

The problem is coming from the fact that the single quote has special meaning to the shell, so it's not surprising that there's a clash when single quotes are also being used in your awk program, when that program is on the command line.

This can be avoided by putting the awk program in its own file:

$ cat a.awk
BEGIN { printf "cat '%s'\n", a }

$ awk -v a="$var" -f a.awk
cat 'foo bar.txt'
jas
  • 10,715
  • 2
  • 30
  • 41
  • hi jas, excellent!!!, it seems to work like that. What does "\047" means? – Ger Cas Nov 29 '18 at 22:29
  • 047 is the value in octal for the ASCII `'` character, and awk allows you to use \nnn within a string to include any character using its octal value. – jas Nov 29 '18 at 22:40
  • Thanks so much for explanation to my doubt. – Ger Cas Nov 29 '18 at 22:59
  • 1
    Correct use of single quotes (`\047`s) but you should be using ENVIRON or passing $var as an arg after the script rather than using `-v` which will expand escape sequences (e.g. convert `\t` to a literal tab character). – Ed Morton Nov 30 '18 at 01:16
1

remove the single quotes around a and add escaped double quotes instead.

$ echo success > "a b"
$ var="a b"; z=$(awk -v a="$var" 'BEGIN{print "cat \"" a "\""}');
$ eval "${z}"

success

however, most likely you're doing some task unnecessarily complex.

karakfa
  • 66,216
  • 7
  • 41
  • 56
  • Thanks for your help. It works nice! Selected as better answer due to looks a little bit shorter than jas's solution. The thing I do this is because the real awk script generates a long ImageMagick script that uses filenames and paths with spaces. I know it looks like I'm complicating more than needed. hehe – Ger Cas Nov 29 '18 at 22:52
  • perhaps you're not fully utilizing the power of pipes? Instead of building a complex command out of strings, you should compose functionality over pipes. – karakfa Nov 29 '18 at 22:54
  • Thanks for the suggestion. – Ger Cas Nov 29 '18 at 23:04
  • 1
    GerCas - @jas's answer is closer to being correct though. Brevity should never be the criteria you use to select software, you need to **think** about what the software does and ask questions if you don't understand the differences between 2 programs. Try both solutions given `var='$(echo "Boo!")'` and now consider what would happen if you replaced `echo "Boo!"` with `rm -r '/'` (don't do it!). – Ed Morton Nov 30 '18 at 00:52
1
$ cat > path\ to/test
foo
$ z=$(awk -v a="$var" 'BEGIN{gsub(/ /,"\\ ",a); str = "cat " a ; print str}')   
$ echo "$z"
cat path\ to/test
$ eval "$z"
foo

The key (in this solution) being: gsub(/ /,"\\ ",a) ie. escaping the spaces with a \ (\\ due to awk).

James Brown
  • 36,089
  • 7
  • 43
  • 59
1

With bash's printf %q "$var" you can correctly escape any string for later use in eval - even linebreaks will be handled correctly. However, the resulting string may contain special symbols like \ that could be interpreted by awk when assigning variables with awk -v var="$var". Therefore, better pass the variable via stdin:

path='/path/with spaces/and/special/symbols/like/*/?/\/...'
cmd=$(printf %q "$path" | awk '{print "cat "$0}')
eval "$cmd"

In this example the generated command $cmd is

cat /path/with\ spaces/and/special/symbols/like/\*/\?/\\/...
Socowi
  • 25,550
  • 3
  • 32
  • 54
  • Thanks for your solution. I didnt know about %q to escape. Thanks to all for each solution to the same problem. – Ger Cas Nov 29 '18 at 22:58