awk 'processing_script_here' my=file.txt
semble s'arrêter et attendre indéfiniment ...
Que se passe-t-il ici et comment puis-je le faire fonctionner?
awk 'processing_script_here' my=file.txt
semble s'arrêter et attendre indéfiniment ...
Que se passe-t-il ici et comment puis-je le faire fonctionner?
Réponses:
Comme le dit Chris , les arguments du formulaire variablename=anything
sont traités comme des affectations de variables (qui sont effectuées au moment où les arguments sont traités par opposition aux (plus récents) -v var=value
qui sont effectués avant les BEGIN
instructions) au lieu des noms de fichiers d'entrée.
Cela peut être utile dans des choses comme:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Où vous pouvez spécifier un fichier différent FS
/ RS
par. Il est également couramment utilisé dans:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Qui est une version plus sûre de:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(qui ne fonctionne pas s'il file1
est vide)
Mais cela gêne lorsque vous avez des fichiers dont le nom contient des =
caractères.
Maintenant, ce n'est qu'un problème lorsque ce qui reste du premier =
est un awk
nom de variable valide .
Ce qui constitue un nom de variable valide dans awk
est plus strict que dans sh
.
POSIX exige que ce soit quelque chose comme:
[_a-zA-Z][_a-zA-Z0-9]*
Avec uniquement des caractères du jeu de caractères portable. Cependant, /usr/xpg4/bin/awk
Solaris 11 au moins n'est pas conforme à cet égard et autorise tous les caractères alphabétiques dans les paramètres régionaux dans les noms de variables, pas seulement a-zA-Z.
Ainsi, un argument comme x+y=foo
ou =bar
ou ./foo=bar
est toujours traité comme un nom de fichier d'entrée et non comme une affectation car ce qui reste du premier =
n'est pas un nom de variable valide. Un argument comme Stéphane=Chazelas.txt
may ou may not, selon l' awk
implémentation et les paramètres régionaux.
C'est pourquoi avec awk, il est recommandé d'utiliser:
awk '...' ./*.txt
au lieu de
awk '...' *.txt
par exemple pour éviter le problème si vous ne pouvez pas garantir que le nom des txt
fichiers ne contiendra pas de =
caractères.
Gardez également à l'esprit qu'un argument comme celui-ci -vfoo=bar.txt
peut être traité comme une option si vous utilisez:
awk -f file.awk -vfoo=bar.txt
(applique également awk '{code}' -vfoo=bar.txt
avec les awk
des versions busybox avant 1.28.0, voir correspondant rapport de bogue ).
Encore une fois, l'utilisation ./*.txt
fonctionne autour de cela (l'utilisation d'un ./
préfixe aide également avec un fichier appelé -
qui autrement awk
comprend comme signifiant une entrée standard à la place).
C'est aussi pourquoi
#! /usr/bin/awk -f
les bangs ne fonctionnent pas vraiment. Alors que var=value
ceux-ci peuvent être contournés en fixant les ARGV
valeurs (ajoutez un ./
préfixe) dans une BEGIN
instruction:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
Cela n'aidera pas les options car celles-ci sont vues par awk
et non le awk
script.
Un problème cosmétique potentiel avec l'utilisation de ce ./
préfixe est qu'il se termine FILENAME
, mais vous pouvez toujours l'utiliser substr(FILENAME, 3)
pour le supprimer si vous ne le souhaitez pas.
L'implémentation GNU awk
corrige tous ces problèmes avec son -E
option.
Après -E
, gawk n'attend que le chemin du awk
script (où -
signifie toujours stdin) puis une liste de chemins de fichiers d'entrée uniquement (et là, même pas -
traité spécialement).
Il est spécialement conçu pour:
#! /usr/bin/gawk -E
shebangs où la liste des arguments sont toujours des fichiers d'entrée (notez que vous êtes toujours libre de modifier cette ARGV
liste dans une BEGIN
déclaration).
Vous pouvez également l'utiliser comme:
gawk -e '...awk code here...' -E /dev/null *.txt
Nous utilisons -E
avec un script vide ( /dev/null
) juste pour nous assurer que ces *.txt
derniers sont toujours traités comme des fichiers d'entrée, même s'ils contiennent des =
caractères.
../foo
, /path/to/foo
et les chemins qui sont dans un codage différent) - dans ce cas, substr(FILENAME,3)
ce ne sera pas suffisant, ou c'est un script unique où l'utilisateur sait essentiellement quels sont les noms de fichiers - dans ce cas, il ne devrait probablement pas se soucier de l'un d'eux contenant =
non plus ;-)
./
problèmes, mais qu'il peut être indésirable dans certaines conditions, telles que les cas où le nom de fichier doit être inclus dans la sortie, auquel cas il ./
doit être redondant et inutile, donc vous je vais devoir m'en débarrasser. Voici au moins un exemple . En ce qui concerne l'utilisateur sachant quels sont les noms de fichiers - eh bien, dans ce cas, nous savons également quel est le nom de fichier, mais cela =
empêche toujours un traitement approprié. Le leader peut donc se -
mettre en travers.
./
préfixe pour contourner cette awk
(mauvaise) fonctionnalité, mais vous vous retrouvez avec une ./
sortie en sortie que vous voudrez peut-être supprimer. Voir comment vérifier si la première ligne de fichier contient une chaîne spécifique? par exemple.
./
mais aussi le global (chemin absolu) /
qui fait que awk interprète l'argument comme un fichier.
Dans la plupart des versions d'awk, les arguments après le programme à exécuter sont soit:
x=y
Étant donné que votre nom de fichier est interprété comme le cas # 2, awk attend toujours que quelque chose soit lu sur stdin (car il ne perçoit pas qu'un nom de fichier a été transmis).
Portablement, ce comportement est documenté dans POSIX :
L'un des deux types d'arguments suivants peut être mélangé:
- fichier: nom de chemin d'un fichier qui contient l'entrée à lire, qui est comparé à l'ensemble des modèles du programme. Si aucun opérande de fichier n'est spécifié, ou si un opérande de fichier est «-», l'entrée standard doit être utilisée.
- affectation: un opérande qui commence par un trait de soulignement ou un caractère alphabétique du jeu de caractères portable (voir le tableau dans le volume Définitions de base de IEEE Std 1003.1-2001, Section 6.1, Jeu de caractères portable), suivi d'une séquence de traits de soulignement, de chiffres, et les caractères alphabétiques du jeu de caractères portable, suivis du caractère «=», doivent spécifier une affectation de variable plutôt qu'un nom de chemin.
En tant que tel, de manière portable, vous avez quelques options (# 1 est probablement le moins intrusif):
awk ... ./my=file
, qui contourne cela car ce .
n'est pas "un caractère de soulignement ou alphabétique du jeu de caractères portable".awk ... < my=file
. Cependant, cela ne fonctionne pas bien avec plusieurs fichiers.ln my=file my_file
, puis l'utiliser my_file
comme d'habitude. Aucune copie ne sera effectuée et les deux fichiers seront sauvegardés par les mêmes données et métadonnées d'inode. Après l'avoir utilisé, il est sûr de supprimer le lien créé car le nombre de références à l'inode sera toujours supérieur à 0../my=file
marche pas ? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Cela devrait être portable car ./my
n'est pas un nom de variable valide, donc ne devrait pas être analysé de cette façon.
=
est précédé d' un trait de soulignement ou d' un caractère alphabétique du jeu de caractères portable (voir le tableau dans le volume Définitions de base de IEEE Std 1003.1-2001, Section 6.1, Jeu de caractères portable), suivi d'une séquence de traits de soulignement, de chiffres et d'alphabets du jeu de caractères portable . donc un chemin de fichier comme ++foo=bar.txt
ou =foo
ou ./foo=bar
sont tous OK comme ça .
ou +
n'est pas un [_a-zA-Z]
.
./my=file
sera transmis mot pour mot.
awk '{print $1,$2}' /etc/passwd
. Le fait est que le fait d'ouvrir le shell par opposition à awk ne fait aucune différence quant à savoir s'il le rend recherchable ou non. En fait, dans awk '{exit}' < /etc/passwd
, vous vous attendez awk
à revenir à la fin du premier enregistrement exit
pour vous assurer qu'il laisse la position au sein de stdin. POSIX l'exige. /usr/xpg4/bin/awk
le fait sur Solaris, mais ni gawk
ne mawk
semble le faire sur GNU / Linux.
awk
cette façon.
Pour citer la documentation de Gawk (note soulignée ajoutée):
Tous les arguments supplémentaires sur la ligne de commande sont normalement traités comme des fichiers d'entrée à traiter dans l'ordre spécifié. Cependant, un argument qui a la forme var = value, assigne la valeur value à la variable var — il ne spécifie pas du tout de fichier.
Pourquoi la commande s'arrête-t-elle et attend-elle? Parce que dans le formulaire, awk 'processing_script_here' my=file.txt
il n'y a pas de fichier spécifié par la définition ci-dessus - my=file.txt
est interprété comme une affectation de variable, et s'il n'y a pas de fichier défini awk
lira stdin (également évident à partir de strace
ce qui montre que awk dans une telle commande attend read(0,'...)
syscall.
Ceci est également documenté dans les spécifications POSIX awk , voir la section OPERANDS et les affectations qui en font partie)
L'affectation des variables est évidente dans la mesure awk '{print foo}' foo=bar /etc/passwd
où la valeur de foo
est imprimée pour chaque ligne dans / etc / passwd. La spécification ./foo=bar
ou le chemin complet fonctionne cependant.
Notez que l' exécution strace
sur awk '1' foo=bar
ainsi que de vérifier avec cat foo=bar
montre que ce problème est awk spécifique et exec ne nom de fichier show comme argument passé, de sorte que des obus ont rien à voir avec les assignations de variables env dans ce cas.
De plus, veuillez noter que awk '...script...' foo=bar
cela ne provoquera pas la création de variables d'environnement par le shell, car les affectations de variables d'environnement doivent précéder une commande pour prendre effet. Voir Règles de grammaire du shell POSIX , point numéro 7. De plus, cela peut être vérifié viaawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd