2

I'm trying to run a small gawk script which will run certain shell commands using system(). I want to get the file list with find and run the script like that:

find <arguments> -printf "%f\n" | script.awk

The problem is that these files have special characters such as spaces, quotes and parentheses in them. For example:

$ ls
'Aujourd'\''hui C'\''est Toi (Orchestral).flac'

I tried all sorts of quotes like the following:

system("<command> \""$0"\"")

And this:

system("<command> \'"$0"\'")

I get all sorts of errors, some of these errors are from sh -c and some are from gawk...

Is there a way to pass the record $0 or any other variables from the gawk script to a shell command with out encountering all of theses problems?

Note: I know it could have been easier to just rename the files temporarily but I find it more challenging to avoid it.

EDIT:

None of the suggested answers solved my problem so here is a more specific description of the problem: I'm trying to run a command which uses 2 awk variables. Examples:

If I run:

system("metaflac --show-tag=ARTIST \""FILE"\"")

I don't get an error because I quote FILE and because it doesn't contain double quotes it goes OK.

But if I run:

system("metaflac --set-tag=ARTIST="AWK_VAR_WITH_ARTIST_VALUE" "\"FILE\")

Even when I quote it, I get an error by metaflac that the flac file was not found. When reading the error message, I see the file name was split into separate words.

1

You could replace system("<command> \"$0\"") by awk's ability to write to a process, i.e. print $0 | "pre;<command> \"$v\"", where the pre part would read stdin into shell variable v. In bash this would be read -r v. You end up with this for example:

awk 'BEGIN{ cmd="read -r v; ls -ld \"$v\"" }
     { print $0 | cmd; close(cmd); }'

If you want to handle horrible things like newlines in the filenames, you can go the print0 route in your find and, if your awk accepts RS='\0', you have for bash:

find . -print0 |
awk -v RS='\0' '
 BEGIN{ cmd="read -r -d \"\" v; ls -ld \"$v\"" }
 { print $0 "\x00" | cmd; close(cmd); }'
0

For the specific case of spaces and single quotes, it should be sufficient to wrap the string in double quotes:

$ find -name 'Aujour*' -printf "%f\n" | awk '{system("ls -l \""$0"\"")}'
-rw-rw-r-- 1 user user 0 May 26 11:27 Aujourd'hui C'est Toi (Orchestral).flac

Alternatively, if your shell provides a shell-quote format specifier you could consider using that in place of find's internal -printf:

$ find -name 'Aujour*' -exec printf '%q\n' {} \; | awk '{system("ls -l "$0)}'
-rw-rw-r-- 1 user user 0 May 26 11:27 ./Aujourd'hui C'est Toi (Orchestral).flac

From help printf in bash for example:

%q  quote the argument in a way that can be reused as shell input
  • Wrapping the string in quotes doesn't work if the string contains one of these quote characters (or a bunch of other characters, for double quotes). Passing a pre-shell-quoted name to awk doesn't work if awk also needs to use the file name. – Gilles May 26 '17 at 22:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.