awk-val mi történt?

1 hónappal ezelőtt Ubuntu 20.04-en és 18.04-en jelenleg is:

echo "30.1  32.3  0.0   0.0   0.0   16.4" | awk '{for( x=(NF); x>=2; x--) if($x!=0.0) print $x;}'
16.4
32.3

Ez az elvárt működés.

Ugyanaz a parancs Ubuntu 20.04-en jelenleg:

echo "30.1  32.3  0.0   0.0   0.0   16.4" | awk '{for( x=(NF); x>=2; x--) if($x!=0.0) print $x;}'
16.4
0.0
0.0
0.0
32.3

Ez már nem jó, a nullákat nem kéne, hogy kiírja... wtf?

Már nem fut le a scriptem normálisan, ami egy hónapja még működött. Megváltozott az if szintaktikája az awk-ban?

Hozzászólások

awk, mawk vagy gawk fut éppen nálad awk néven?

Tedd idézőjelbe a 0.0-t. Szerintem numerikusan 0-vá konvertálódik az egyik helyen, míg a másikon nem, és ezért. Az awk-ban is vannak típusok, csak nem tűnik fel az automatikus konverzió miatt.

       typeof(x)  Return a string indicating the type of x.  The  string  will
                  be  one  of "array", "number", "regexp", "string", "strnum",
                  "unassigned", or "undefined".

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Szerkesztve: 2020. 06. 05., p - 15:49

Még ezt is nézd meg:

       strtonum(str)           Examine str, and return its numeric value.   If
                               str  begins  with  a  leading 0, treat it as an
                               octal number.  If str begins with a leading  0x
                               or  0X, treat it as a hexadecimal number.  Oth‐
                               erwise, assume it is a decimal number.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Nekem megy így:

ice@ubuntu:~/.ssh$ cat /etc/issue
Ubuntu 20.04 LTS \n \l

ice@ubuntu:~/.ssh$ echo "30.1  32.3  0.0   0.0   0.0   16.4" | awk '{for( x=(NF); x>=2; x--) if($x != "0.0") print $x;}'
16.4
32.3

Így működik, de egyrészt én numerikus összehasonlítást szeretnék, másrészt meg lehet, hogy 50 másik helyen is megváltozott a program működése? Nem akarom én átírni, meg debugolni, ha eddig jó volt.
Engem az érdekelne, hogy mi változott meg és miért. Na meg, hogy a kód átírása nélkül újra működjön.

$ mawk -W version
mawk 1.3.4 20190203
Copyright 2008-2018,2019, Thomas E. Dickey
Copyright 1991-1996,2014, Michael D. Brennan

random-funcs:       arc4random_stir/arc4random
regex-funcs:        internal
compiled limits:
sprintf buffer      8192

maximum-integer     2147483647

$ echo $LANG
hu_HU.UTF-8

$ echo "30.1  32.3  0.0   0.0   0.0   16.4" | mawk '{for( x=(NF); x>=2; x--) if($x!=0.0) print $x;}'
16.4
0.0
0.0
0.0
32.3
$ ( LANG=C ; export LANG ; echo "30.1  32.3  0.0   0.0   0.0   16.4" | mawk '{for( x=(NF); x>=2; x--)
16.4
32.3
$

Lokalizacio? Tizedespont helyett tizedesvesszo valamiert? Hasznalj igazi lokalizaciot (LANG=C) minden kritikus dologhoz. 

( Legalább egy export LANG ; -ot mondj még az echo előtt! És ha nem akarod elbarmolni a környezetedet, akkor az egész sort rakd ( ) közé. Ugyanis baromira shell függő, hogy mi történik egy exportált változóval, ha az export után új értéket kap. )

 

Amúgy szerintem is lokalizációs gondnak tűnik.

( LANG=C ; export LANG ; echo "30.1  32.3  0.0   0.0   0.0   16.4" | awk '{for( x=(NF); x>=2; x--) if($x!=0.0) print $x;}' )

Így működik.

De eddig működött így is:
$ echo $LANG
hu_HU.UTF-8

És a 18.04-es gépen szintén működik most is. Szóval a rejtély még nincs teljesen megoldva.

Nem lehet, hogy az Ubuntunál rájöttek, hogy Magyarországon tizedesvessző van, nem pedig tizedespont? Aztán jött egy frissítés...

Szerk.: Azt sohasem értettem, amikor megvan a megoldás, és továbbra megy az értetlenkedés, hogy ez pontosan úgy legyen jó, ahogy eddig. Akkor pontosan azt a rendszert használd frissítetlenül, mint amelyikkel jó volt.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Az lehet, de akkor meg ennek kéne működnie, nem?

$ echo "30,1  32,3  0,0   0,0   0,0   16,4" | awk '{for( x=(NF); x>=2; x--) if($x!=0,0) print $x;}'
awk: line 1: syntax error at or near ,

 

Elfogadom a változást, ha tényleg ez a megoldás és direkt csinálták, nem regresszió. De mondjuk ha nem major verzió váltásról van szó, akkor csak nem kellene, hogy elromoljon egy script.

OFF:

mióta van, hogy a mawk a default AWK az Ububan? Meg mernék esküdni rá, hogy régebben valamelyik GAWK verziót adták. - Bár a net szerint már 14.04-ben is mawk volt. Amúgy nem lehet, hogy a volt a 20.04-ben awk verzió frissítés ebben az elmúlt egy hónapban?

/OFF

sudo update-alternatives --config awk

Esetleg?

mawk (1.3.4.20200120-2) unstable; urgency=high

  * debian/copyright: Refresh dates. 
  * debian/watch: Add watch file to monitor upstream releases.

 -- Boyuan Yang <byang@debian.org>  Sun, 16 Feb 2020 14:41:09 -0500

mawk (1.3.4.20200120-1) unstable; urgency=medium

  * Upload to unstable. 

 -- Boyuan Yang <byang@debian.org>  Tue, 28 Jan 2020 11:27:10 -0500

mawk (1.3.4.20200120-1~exp1) experimental; urgency=medium

  * New upstream release snapshot.
  * debian/copyright: Update copyright year information.
  * debian/upstream/metadata: Add metadata: Repository,
    Repository-Browse.

 -- Boyuan Yang <byang@debian.org>  Sat, 25 Jan 2020 18:09:03 -0500

mawk (1.3.4.20190203-1~exp1) experimental; urgency=medium

  * New upstream release snapshot (Closes: #554167)
    + Fixes segmentation fault on certain awk file.
      (Closes: #901477)
    + Fixes crashes with long strings. (Closes: #173664)
    + Fixes bugs around substr. (Closes: #649147)
  * debian/control:
    + Declare autopkgtest as test suite. (Closes: #692662)
    + Bump Standards-Version to 4.5.0.
    + Update the pre-depends field and use ${misc:Pre-Depends}
      instead.
  * debian/copyright: Refresh information.
  * debian/: Lintian warning fixes:
    - description-synopsis-starts-with-article

  [ Adreas Henriksson ]
  * debian/patches: Review and drop all obsolete patches.
  * Fix up autopkgtest for new version of mawktest.
  * debian/control: Use autoconf-dickey instead of mainline
    autoconf. This is required for mawk and other programs
    maintained upstream by Dickey.

 -- Boyuan Yang <byang@debian.org>  Sat, 25 Jan 2020 17:50:25 -0500

Az elmélet az, amikor mindent ismerünk, de semmi nem működik. A gyakorlat az, amikor minden működik, de senki nem tudja, miért.

Szerintem eleve nem jó a scripted. Nekem ez nem tetszik:

if($x!=0.0)

Mert ez numerikus 0, és szerintem implementáció kérdése, hogy a 0, 0.0, 0.00, 0x00, 00 közül mindegyik, vagy csak egyesek felelnek meg az egyenlőségnek. Jó, a 0x00 lehet, hogy csak strtonum() hívását követően lesz nulla. Szóval, ha számként nullát akarnék, akkor 0-t írnék, nem kell bele pont. Ha meg stringként, akkor "0.0" a megoldás. Így mindenképp zavaró.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Erről beszélek:

awk 'BEGIN {if (0 == 0.0) printf("true\n"); else printf("false\n");}'
true

awk 'BEGIN {if ("0" == "0.0") printf("true\n"); else printf("false\n");}'
false

awk --version | head -n1
GNU Awk 5.0.1, API: 3.0 (GNU MPFR 4.0.2-p7, GNU MP 6.1.2)

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Én ezt értem, de itt szó sincs stringként való összehasonlításról. Nem értem, hogy kanyarodtunk ide.
Ez az eredeti probléma:

mawk Ubuntu 20.04:

$ awk 'BEGIN { x=12.3; if(x==12.3) printf("true\n"); else printf("false\n");}'
true

$ echo "12.3" | awk '{ if($1==12.3) printf("true\n"); else printf("false\n"); }'
false

mawk Ubuntu 18.04:

$ awk 'BEGIN { x=12.3; if(x==12.3) printf("true\n"); else printf("false\n");}'
true

$ echo "12.3" | awk '{ if($1==12.3) printf("true\n"); else printf("false\n"); }'
true

gawk Ubuntu 20.04:

$ awk 'BEGIN { x=12.3; if(x==12.3) printf("true\n"); else printf("false\n");}'
true

$ echo "12.3" | awk '{ if($1==12.3) printf("true\n"); else printf("false\n"); }'
true

Miért false a második? Illetve ha elfogadjuk, hogy false a második, akkor miért true az első?
Bugos a mawk vagy nem?

Miközben reggeliztem, majd megcsináltam a fahéjas gyömbér teámat, megértettem a jelenséget. :)

A mawk interpreteren belül a kódra nézvést nincs lokalizáció. Szépen is néznénk ki, ha a környezeti változóktól függene, amit a scriptben leírsz. Tehát ott a 12.3 egy szám, bármi is legyen a lokalizáció. Ezzel szemben az input - és vélelmezem, egy printf("%.2f") esetén az output is, bár nem biztos - feldolgozása a lokalizációs függvények hívásán keresztül történik. Azaz 12,3 egy szám, de más lokalizációval 12.3 egy szám.

Amikor az inputod "0.0" volt, s összehasonlítottad 0.0-val, akkor a magyar lokalizáció szerint ezt nem lehetett számmá konvertálni, így "0.0" string lett belőle. Az összehasonlításkor leírt 0.0 konstans előbb numerikus 0-vá konvertálódott, majd, hogy ne legyen type mismatch, kénytelen lett "0" stringgé konvertálódni. Így a programodban az if("0.0" != "0") hajtódott végre, ami true, s ezért írta végül ki a "0.0"-t.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Ugye láttad, hogy az echo paraméterében vessző volt a "tizedespont" ?

(Szóval a pipe semmit nem változtat, de ahogy a kollága írja, valószínűleg a magyar lokalizációval megadott törtet  a "valódi" 12.3 -ra átalakítja az input során, ami nyilván megegyezik a 12.3-mal.)

Láttam, azért kérdezem, hogy mióta nyúl bele a pipe a tartalomba... duh.

Olyan nincs, hogy "semmit nem változtat" és "de átalakítja az input során". Vagy ez vagy az.  Milyen alapon alakítja át? Nem excel az. Legközelebb a 10.15.2018-at fogja átalakítani 2018-10-15-re?

Ugyanakkor gawk:
$ echo "12,3" | awk '{ if($1==12.3) printf("true\n"); else printf("false\n"); }'
false

Újra kérdezem:

- Ha mawk van, akkor a pipe átalakítja a vesszőt ponttá lokalizáció szerint? Ha gawk van, akkor meg nem. Akkor pont, hogy a ponttal működik. Megnézi a pipe, hogy mi jön utána és aszerint alakítgat? Le van ez valahova írva?
 Ha a pipe alakítgatna, akkor ugyanaz lenne a mawk és a gawk működése, nem?
- 1 hónapja még a mawk is működött ponttal. Mi változott?

A pipe bitre pontosan azt viszi át, amit kell. A mawk az, amelyik a környezeti változótól függően vagy 12.3, vagy 12,3 alakban várja az inputben a tizedes törteket. Ha a lokalizációd szerint tizedesvesszőt vár, miközben tizedesponttal kínálod meg, ezt nem fogja számként értelmezni, hanem azt mondja, akkor ez bizony egy string. Az összehasonlítás másik operandusa egy szám volt. Stringet számmal nem lehet összehasonlítani, a string operandust nem lehet számmá alakítani, így az egyetlen lehetőség, hogy a szám operandust konvertálta stringgé, majd utána végezte el az összehasonlítást. Igen ám, de a "0.0" és a "0" stringek valóban nem azonosak. Már a hosszuk is különbözik. Emiatt teljesült a != feltétel, s írta ki, amit nem szerettél volna.

A gawk szerintem egész egyszerűen nem foglalkozik az inputban a számok lokalizációjával. Ebben az értelemben éppen az mawk a fejlettebb.

A megoldás tehát az, ahogy írtam is, hogy a mawk-t a megfelelő környezeti változó megadását követően hívod meg.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Szerkesztve: 2020. 06. 06., szo - 00:59

Csak megerositeskeppen, GNU Awk hasznalataval az elso peldadban szereplo modon megy, minden Linuxomon az van (megneztem), de meg FBSD-n is ezt az eredmenyt adja.

A scripted így is működne:

echo "30.1  32.3  0.0   0.0   0.0   16.4" | tr '.' ',' | awk '{for( x=(NF); x>=2; x--) if($x!=0.0) print $x;}'

Persze a helyes megoldás nem ez, hanem a lokalizáció idomitása az inputodhoz:

echo "30.1  32.3  0.0   0.0   0.0   16.4" | LC_ALL=C awk '{for(x = (NF); x >= 2; x--) if($x != 0) print $x;}'

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

TL;DR Kimaradt valamelyik profile-ból a LC_NUMERIC=C