Dans le problème ci-dessous, je peux obtenir une ligne aléatoire à partir de file1 en utilisant awk, et je peux éditer file2 en utilisant awk, si j'écris simplement une chaîne fixe dans le fichier, mais si je veux utiliser une ligne aléatoire de file1 au lieu d'une chaîne fixe , alors je suis coincé. Je ne sais pas comment insérer le code awk qui obtient la ligne aléatoire, dans le code qui édite file2 (ou une approche équivalente qui fait le même travail).

Fichier1:

    string1_^%"$"£xxXF%
    string2-$^^!dV"$&"}
    string3~£""!&vhTT[@
    string4_%^c!}[&(SR#
    string5_$%&<[*^"!"H
    string6_$$£<[*~)!£H

Fichier2:

    @ data1 line1, unique text follows
    data1 line2, unique text follows
    @ data2 line1, unique text follows
    data2 line2, unique text follows
    @ data3 line1, unique text follows
    data3 line2, unique text follows
    @ data4 line1, unique text follows
    data4 line2, unique text follows

Sortie désirée:

Tenue:

    $ data1 line1, unique text follows
    data1 line2, unique text follows
    fixed_text_inserted
    string2-$^^!dV"$&"}
    $ data2 line1, unique text follows
    data2 line2, unique text follows
    fixed_text_inserted
    string6_$$£<[*~)!£H
    $ data3 line1, unique text follows
    data3 line2, unique text follows
    fixed_text_inserted
    string3~£""!&vhTT[@
    $ data4 line1, unique text follows
    data4 line2, unique text follows
    fixed_text_inserted
    string6_$$£<[*~)!£H

Fixed_text_inserted est toujours le même, juste une chaîne (pas d'espaces ni de tabulations)

La dernière (c'est-à-dire la 4ème) ligne de chaque enregistrement est une ligne aléatoire de file1 (ces lignes contiennent des caractères spéciaux, par exemple $ £% "'% ^, et du texte, mais pas d'espaces ni de tabulations).

Le choix aléatoire est avec remplacement, comme dans mon exemple de fichier, où string6 _ $$ £ <[* ~)! £ H} se trouve avoir été choisi plus d'une fois.

J'ai environ 400 fichiers file2 et chacun est gros (~ 1 Go), et il m'est donc facile d'écrire un script trop lent, mais j'ai un problème pour l'implémenter avec awk.

Voici une idée des deux commandes awk qui font chacune presque la moitié de ce que je veux:

Obtient d'abord une ligne aléatoire de file1:

awk 'BEGIN{srand();} {a[NR]=$0} END{for(i=1; i<=5000; i++){x=int(rand()*NR) + 1}}' file 1

Ensuite, utilisez cette ligne aléatoire dans la création d'un nouveau fichier (outfile) à partir de file2:

awk 'BEGIN {getline rlines; RS = "#" ; FS = "\n"} NR > 1 {print $1"\n"$2"\n"; printf rlines[NR]; printf "\n"}' file2 > outfile

Je doute que la fusion de ces deux lignes soit le moyen d'y parvenir; ces lignes bizarres sont juste pour donner une idée (j'espère) de ce que j'essaye de faire.

Je publie ceci non seulement parce que je suis bloqué, mais parce que le problème touche à une question générique de savoir comment travailler sur plusieurs fichiers dans awk et comment y définir des variables complexes. Il existe des exemples en ligne, mais ils traitent ces choses séparément.

Merci à tous ceux qui ont des suggestions, je n'ai pas encore le sens du "awk thinking".

1
knovice 22 avril 2020 à 21:15

3 réponses

Meilleure réponse

Cela insère une ligne sélectionnée au hasard de fichier1 dans fichier2 à la troisième position. Cela devrait être très rapide pour cette tâche.

$ sed "3i$(shuf -n1 file1)" file2 > file3

Si vous souhaitez insérer toutes les trois lignes, en répétant éventuellement la ligne aléatoire, coupez également la chaîne aléatoire à la limite non alphanumérique. Pour répéter la même chose pour plus d'un fichier d'entrée, cela devrait faire ...

$ awk 'BEGIN   {srand()}
       NR==FNR {sub(/[^[:alnum:]].*/,""); a[NR]=$0; size++; next}
       FNR==1  {close(f); f=FILENAME".outfile"} 
               {print > f} 
       !(FNR%2){print a[int(rand()*size)+1] > f}' randomvals file1 file3 ...

Fournissez d'abord le fichier de valeurs aléatoires et les fichiers à mettre à jour ensuite. Créera des versions ajoutées ".updated" pour chaque fichier d'entrée.

1
karakfa 24 avril 2020 à 20:33

On dirait que cela revient à remplacer chaque troisième ligne d'un fichier par une ligne aléatoire d'un deuxième fichier. C'est assez facile à faire avec une combinaison de awk et shuf:

$ cat file1.txt
#a
b
c
#d
e
f
#g
h
i
$ cat file2.txt
1
2
3
4
5
6
$ awk 'NR == FNR { lines[NR]=$0; next }
       { if (FNR % 3) print; else print lines[++i] }' <(shuf file2.txt) file1.txt
#a
b
6
#d
e
2
#g
h
1

Lisez une version mélangée du fichier de ligne de remplacement dans un tableau, puis pour chaque troisième ligne du fichier d'enregistrement, imprimez une ligne de remplacement au lieu de l'original.


Et pour le plaisir, un équivalent bash qui utilise un coproc pour accéder aux fichiers mélangés au lieu de les stocker dans un tableau.

#!/usr/bin/env bash
# replace.sh originalfile replacementlinesfile
coproc shuf { shuf "$2"; read; }
declare -i i=1
while IFS= read -r line; do
    if [ $((i++ % 3)) -eq 0 ]; then
        IFS= read -r -u "${shuf[0]}" line
    fi
    echo "$line"
done < "$1"
echo "done" >&"${shuf[1]}"
wait "$shuf_PID"
1
Shawn 23 avril 2020 à 06:51

Votre question n'est pas claire, mais ceci:

awk '
    NR==FNR { a[NR]=$0; next }
    FNR==1 { srand(); rline=a[int(1+rand()*(NR-1))] }
    { print }
    FNR==3 { print rline }
' file1 file2

Insérera une ligne aléatoire de file1 après la 3ème ligne de file2. Est-ce quelque chose comme ça que vous essayez de faire?

Par exemple:

$ cat file1
A
B
C
D

$ cat file2
1
2
3
4
5

.

$ awk 'NR==FNR{a[NR]=$0; next} FNR==1{srand(); rline=a[int(1+rand()*(NR-1))]} {print} FNR==3{print "<"rline">"}' file1 file2
1
2
3
<C>
4
5

.

$ awk 'NR==FNR{a[NR]=$0; next} FNR==1{srand(); rline=a[int(1+rand()*(NR-1))]} {print} FNR==3{print "<"rline">"}' file1 file2
1
2
3
<D>
4
5
0
Ed Morton 22 avril 2020 à 19:18