Bug di sort e uniq? Strani effetti con UTF8

[code]$ echo “±∅∞∅∓” | sed -e ‘s/(.)/\1\n/g’ | sort

±



∓[/code]
(Il carattere “insieme vuoto” non viene ordinato rispetto a “infinito”)

[code]$ echo “±∅∞∅∓” | sed -e ‘s/(.)/\1\n/g’ | sort | uniq

±
∅[/code]

Qui il carattere “infinito” scompare dall’output (stesso risultato con sort -u).

Secondo voi è un bug da segnalare?

Edit: sicuramente la colpa non è di sed:

$ (echo "∅"; echo "∞"; echo "∅") | sort ∅ ∞ ∅

Si, secondo me è un bug. Pare che solo ± venga riordinato.

Sì, è come se “∅” (8709, ovvero 0x2205) e “∞” (8734, ovvero 0x221e) vengano considerati lo stesso carattere…
Aperta segnalazione di https://bugzilla.redhat.com/show_bug.cgi?id=1336308.

Edit: forse la cosa è nota; infatti leggo nel sorgente sort.c:

/* FIXME: None of these tables work with multibyte character sets. Also, there are many other bugs when handling multibyte characters. One way to fix this is to rewrite 'sort' to use wide characters internally, but doing this with good performance is a bit tricky. */

Nel frattempo puoi usare qualcosa tipo:

$ echo "±∅∞∅∓" | sed -e 's/\(.\)/\1\n/g' | python3 -c"import sys;print(*sorted(sys.stdin),end='',sep='')"

…oppure, se preferisci Python 2:

$ echo "±∅∞∅∓" | sed -e 's/\(.\)/\1\n/g' | python -c'from sys import *;map(stdout.write,sorted(stdin))'

Per curiosità: come hai scoperto questo strano comportamento?

Ho una serie di files in java (+ qualche xml), e mi è venuta la curiosità di vedere quali caratteri non ascii erano presenti; inoltre, volevo vedere se avevo già inserito in qualche commento il simbolo dell’insieme vuoto per fare copia-incolla. Ho dato un

$ find /path/di/lavoro -name "*.java" -or -name "*.xml" | while read FILE; do cat "$FILE" | sed -e 's/\(.\)/\1\n/g'; done | sort | uniq | tr -d ' -~\n'; echo

e “∅” non c’era. L’ho copiato da una pagina web, salvato il file, ripetuto il comando, e… è sparito il simbolo “∞” dall’output! Da lì ho cominciato a fare qualche test…

N.B. Non credo che python possa servire nel mio caso, perché la rimozione dei duplicati con uniq soffre dello stesso bug da cui è affetto sort… e non posso certo stampare diversi mega di caratteri ripetuti…

Con Python puoi ottenere gli elementi doppi:

$ echo "±∅∞∅∓" | sed -e 's/\(.\)/\1\n/g' | python3 -c"import fileinput as f;i=list(f.input());print(set((l for l in i if i.count(l)>1)))"

Volendo puoi usare globstar al posto del find:

$ (shopt -s globstar for file in in $path/**/*.{xml,java}; do # codice done)

[quote=frafra]Con Python puoi ottenere gli elementi doppi:

$ echo "±∅∞∅∓" | sed -e 's/\(.\)/\1\n/g' | python3 -c"import fileinput as f;i=list(f.input());print(set((l for l in i if i.count(l)>1)))"[/quote]

Non sembra affidabile (oltre a riportare parentesi graffe, apici e virgole, in stile stampa di lista python):

$ echo "abderaaaaa±∅∞∅∓" | sed -e 's/\(.\)/\1\n/g' | python3 -c"import fileinput as f;i=list(f.input());print(set((l for l in i if i.count(l)>1)))" {'∅\n', 'a\n'}

Forse funziona così:

$ echo "±∅∞∅∓aabcd" | sed -e 's/\(.\)/\1\n/g' | python3 -c"import fileinput as f;i=list(f.input());print(''.join(set(l for l in i)))" | tr -d '$\n'; echo a∅d∞bc±∓

Quindi:

$ find $PATH_FILES -name "*.java" -or -name "*.xml" | while read FILE; do cat "$FILE" | sed -e 's/\(.\)/\1\n/g'; done | python3 -c"import fileinput as f;i=list(f.input());print(''.join(set(l for l in i)))" | tr -d ' -~\n'; echo ù≠πü³éγÁôáìÈó≥ΔÉ∞Σβªθ¹°Óδ²·¡à¿∅αò±∓èÚÍ≤εíú§ñ

Se togli la parte finale dell’if non funziona correttamente.
Detto ciò, ho riletto il tuo messaggio precedente e recepito le tue nuove osservazioni. Ho provato a scrivere qualcosa di meglio.

$ echo "abderaaaaa±∅∞∅∓" | python3 -c"import fileinput as f;print(*set(filter(lambda c:ord(c)>127,''.join(f.input()))),sep='\n')" ± ∓ ∅ ∞

La cosa carina è che puoi passargli caratteri sia via pipe sia via file.

$ python3 -c"import fileinput as f;print(*set(filter(lambda c:ord(c)>127,''.join(f.input()))),sep='\n')" ~frafra/Documenti/*.txt € è à

Di conseguenza, nel tuo caso suggerirei di lanciare:

$ (shopt -s globstar; python3 -c"import fileinput as f;print(*set(filter(lambda c:ord(c)>127,''.join(f.input()))),sep='\n')" $PATH_FILES/**/*.{java,xml})

Siccome potrebbero esserci caratteri non utf-8 (raro, ma accade), per semplicità, al posto di complicare la vita a Python, potresti usare il buon iconv (che se vede qualcosa che non gli piace lo ignora):

$ (shopt -s globstar;cat $PATH_FILES/**/*.{java,xml}|iconv -tutf-8| python3 -c"import fileinput as f;print(*set(filter(lambda c:ord(c)>127,''.join(f.input()))),sep='\n')")

Se ti interessasse anche l’ordinamento…

$ (shopt -s globstar;cat $PATH_FILES/**/*.{java,xml}|iconv -tutf-8| python3 -c"import fileinput as f;print(*sorted(set(filter(lambda c:ord(c)>127,''.join(f.input())))),sep='\n')")