Les séquences d'échappement sont passées en tant qu'args à gueuler non interprétées

Je veux être capable de passer un argument sur la command line à gawk qui est évalué pour les séquences d'échappement.

Le problème:

 $ gawk 'BEGIN { print ARGV[1]; }' '\t' \t 

Au lieu de cela, je voudrais get un caractère d'onglet réel.

De la gawk docs :

Les séquences d'échappement de la list précédente sont toujours traitées en premier, à la fois pour les constantes de strings et les constantes d'expressions rationnelles. Cela arrive très tôt, dès que awk lit votre programme.

Comment puis-je interpréter les évasions de caractères dans les arguments de command line?

Le but final est myscript.awk --sep '\t' , où séparator est une string de format, donc passer un onglet littéral n'est pas une option. Je suis aussi familier avec la façon facile que je pourrais effectuer cela dans bash, mais je suis intéressé par une façon de le faire en [g] awk.

Comment puis-je imprimer la version non échappée des arguments de command line?

 print ARGV[1] 

Le problème est que vous ne voulez pas l'argument de command line sans échappement. Tu veux l'interpréter. Vous passez \t (la barre oblique inverse de deux caractères, minuscule T), et vous voulez que cela soit traduit en barre oblique inverse. Vous devrez le faire manuellement. Il suffit de traduire \t pour un onglet – gsub(/\\t/, "\t") – mais si vous voulez aussi supporter les échappements octaux et supprimer la barre oblique inverse avant un caractère non reconnu, c'est lourd dans awk.

 split ARGV[1], a, "\\"; s = a[1]; delete a[1]; for (x in a) { if (skip_next) { skip_next = 0; } else if (x == "") { s = s "\\"; skip_next = 1; } else if (x ~ /^[0-7][0-7][0-7]/) { s = s sprintf("%c", 64*substr(x,1,1) + 8*substr(x,2,1) + substr(x,3,1)); sub(/^.../, x); } else if (x ~ /^[0-7][0-7]/) { s = s sprintf("%c", 0 + 8*substr(x,1,1) + substr(x,2,1)); sub(/^../, x); } else if (x ~ /^[0-7]/) { s = s sprintf("%c", 0 + substr(x,1,1)); sub(/^./, x); } else { sub(/^a/, "\a", x) || sub(/^b/, "\b", x) || sub(/^n/, "\n", x) || sub(/^r/, "\r", x) || sub(/^t/, "\t", x) || sub(/^v/, "\v", x); } s = sx; } 

(Attention: code non testé!) Au lieu de ce code complexe, vous pourriez appeler printf dans un sous-shell. Même ce n'est pas si facile à faire quand la string pourrait être multiligne.

 s = ARGV[1] gsub(/'/, "'\\''", s) cmd = "printf %b '" s "'." s = "" while ((cmd | getline line) > 0) s = s line "\n" sub(/..$/, "", s) 

Notez que lorsque vous écrivez "\t" dans un script awk, c'est une string contenant le caractère tab. C'est la façon dont la syntaxe awk est: la barre oblique inverse a une signification particulière dans une string littérale. Note: dans une string littérale , pas dans une string . Si une string contient une barre oblique inverse, c'est juste un autre caractère. L'extrait de code source "\t" , constitué de quatre caractères, est une expression dont la valeur est la string d'un caractère contenant un onglet, de la même manière que l'extrait de code source 2+2 composé de trois caractères est une expression dont la valeur est le nombre 4 .

Il serait préférable que votre script awk prenne l'argument de séparation comme une string littérale. Cela faciliterait l'utilisation: votre interface nécessite que l'appelant évite les antislash dans l'argument. Si vous souhaitez que le séparateur soit un onglet, transmettez un caractère d'onglet réel.

Tout d'abord, vous n'êtes pas en train de passer un onglet à votre awk . Rappelez-vous que le shell évalue les arguments avant de les passer à awk et que '\t' entre guillemets est évalué comme un littéral \ suivi d'un \t :

 $ set -x $ gawk 'BEGIN { print ARGV[1]; }' '\t' + gawk 'BEGIN { print ARGV[1]; }' '\t' \t 

Comme vous pouvez le voir ci-dessus, vous ne passez pas un onglet à gawk donc vous pouvez difficilement vous attendre à en imprimer un. Comparez avec la version ci-dessous qui passe un onglet:

 $ gawk 'BEGIN { print ARGV[1]; }' "$(printf '\t')" ++ printf '\t' + gawk 'BEGIN { print ARGV[1]; }' ' ' ## note the tab ## This line contains a printed tab 

Vous pouvez également passer l'onglet en tant que variable:

 gawk -vt='\t' 'BEGIN {print t}' 

Ici, le '\t' est développé par awk, pas le shell, donc l'onglet est interprété correctement.

La solution est d'utiliser getline .

Dans un file:

 BEGIN { sep = ARGV[1] gsub(/'/, "'\\''", sep); gsub(/%/, "%%", sep); "printf -- '" sep "'" | getline sep; printf sep; }