Risposte:
Con sed
:
$ echo "123456789" | sed 's/\([[:digit:]]\{3\}\)\([[:digit:]]\{3\}\)\([[:digit:]]\{3\}\)/\1,\2,\3/g'
123,456,789
(Nota che funziona solo per esattamente 9 cifre!)
o questo con sed
:
$ echo "123456789" | sed ':a;s/\B[0-9]\{3\}\>/,&/;ta'
123,456,789
Con printf
:
$ LC_NUMERIC=en_US printf "%'.f\n" 123456789
123,456,789
echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}'
sed
funziona solo se il numero è esattamente di 9 cifre. Il printf
non funziona su zsh. Quindi la seconda sed
risposta è probabilmente la migliore.
echo 123456789 | awk '{printf ("%'\''d\n", $0)}'
(che evidentemente non funziona sempre su Linux!?, Ma funziona bene su AIX e Solaris)
bash
's printf
supporti praticamente tutto quello che si può fare nella printf
funzione C
type printf # => printf is a shell builtin
printf "%'d" 123456 # => 123,456
printf
da coreutils farà lo stesso
/usr/bin/printf "%'d" 1234567 # => 1,234,567
zsh
, post aggiornato qui .
vsnprintf
. Su un sistema GNU / Linux, glibc sembra averlo supportato almeno dal 1995.
export LC_NUMERIC="en_US"
se vuoi forzare le virgole.
locale -a
. Ho dovuto usareen_US.utf8
Puoi usare numfmt:
$ numfmt --grouping 123456789
123,456,789
O:
$ numfmt --g 123456789
123,456,789
Nota che numfmt non è un'utilità POSIX, fa parte dei coreutils GNU.
-d, --grouping
poiché le doppie sillabazioni richiedono lunghe opzioni?
--g
funziona bene per me invece di --grouping
, cioè numfmt --g 1234567890
e numfmt --grouping 1234567890
fare la stessa cosa. È una piccola utility molto utile.
cat <<'EOF' |
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096
EOF
perl -wpe '1 while s/(\d+)(\d\d\d)/$1,$2/;'
produce:
13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096
Ciò si ottiene dividendo la stringa di cifre in 2 gruppi, il gruppo di destra con 3 cifre, il gruppo di sinistra con tutto ciò che rimane, ma almeno una cifra. Quindi tutto viene sostituito dai 2 gruppi, separati da una virgola. Questo continua fino a quando la sostituzione fallisce. Le opzioni "wpe" sono per la lista degli errori, racchiudono l'istruzione all'interno di un ciclo con una stampa automatica e prendono l'argomento successivo come il "programma" perl (vedi il comando perldoc perlrun per i dettagli).
I migliori auguri ... evviva, drl
BASH
/ AWK
alternativa, quindi potrebbe non averlo usato PERL
prima. In ogni caso, è meglio spiegare cosa fa il comando, specialmente per le battute.
Con alcune awk
implementazioni:
echo "123456789" | awk '{ printf("%'"'"'d\n",$1); }'
123,456,789
"%'"'"'d\n"
è: "%
(virgolette singole) (virgolette doppie) (virgolette singole) (virgolette doppie) (virgolette singole) d \ n"
Che utilizzerà il separatore di migliaia configurato per la tua locale (in genere ,
in locali inglesi, spazio in francese, .
in spagnolo / tedesco ...). Come restituito dalocale thousands_sep
Un caso d'uso comune per me è modificare l'output di una pipeline di comandi in modo che i numeri decimali vengano stampati con migliaia di separatori. Piuttosto che scrivere una funzione o uno script, preferisco usare una tecnica che posso personalizzare al volo per qualsiasi output da una pipeline Unix.
Ho trovato printf
(fornito da Awk) il modo più flessibile e memorabile per ottenere questo risultato. L'apostrofo / virgoletta singola è specificato da POSIX come un modificatore per formattare i numeri decimali e ha il vantaggio di essere a conoscenza delle impostazioni locali, quindi non è limitato all'utilizzo dei caratteri virgola.
Quando si eseguono i comandi Awk da una shell Unix, possono esserci difficoltà a inserire un carattere a virgoletta singola all'interno di una stringa delimitata da virgolette singole (per evitare l'espansione della shell di variabili posizionali, ad es $1
.). In questo caso, trovo che il modo più leggibile e affidabile per inserire il carattere a virgoletta singola sia immetterlo come una sequenza di escape ottale (che inizia con \0
).
Esempio:
printf "first 1000\nsecond 10000000\n" |
awk '{printf "%9s: %11\047d\n", $1, $2}'
first: 1,000
second: 10,000,000
Output simulato di una pipeline che mostra quali directory utilizzano la maggior parte dello spazio su disco:
printf "7654321 /home/export\n110384 /home/incoming\n" |
awk '{printf "%22s: %9\047d\n", $2, $1}'
/home/export: 7,654,321
/home/incoming: 110,384
Altre soluzioni sono elencate in Come sfuggire a una singola citazione in awk .
Nota: come indicato in Print a Single Quote , si consiglia di evitare l'uso di sequenze di escape esadecimali in quanto non funzionano in modo affidabile su sistemi diversi.
\047
.
awk
e bash
avere buone soluzioni integrate, basate su printf
, come descritto nelle altre risposte. Ma prima, sed
.
Per sed
, dobbiamo farlo "manualmente". La regola generale è che se si dispone di quattro cifre consecutive, seguite da una non cifra (o fine riga), è necessario inserire una virgola tra la prima e la seconda cifra.
Per esempio,
echo 12345678 | sed -re 's/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/'
stamperà
12345,678
Ovviamente dobbiamo quindi continuare a ripetere il processo, al fine di continuare ad aggiungere abbastanza virgole.
sed -re ' :restart ; s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/ ; t restart '
In sed
, il t
comando specifica un'etichetta a cui verrà eseguito il salto se l'ultimo s///
comando ha avuto esito positivo. Definisco quindi un'etichetta con :restart
, in modo che salti indietro.
Ecco una demo di bash (su ideone ) che funziona con qualsiasi numero di cifre:
function thousands {
sed -re ' :restart ; s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/ ; t restart '
}
echo 12 | thousands
echo 1234 | thousands
echo 123456 | thousands
echo 1234567 | thousands
echo 123456789 | thousands
echo 1234567890 | thousands
$ echo 1232323 | awk '{printf(fmt,$1)}' fmt="%'6.3f\n"
12,32,323.000
Se stai guardando i GRANDI numeri, non sono riuscito a far funzionare le soluzioni di cui sopra. Ad esempio, otteniamo un numero davvero grande:
$ echo 2^512 |bc -l|tr -d -c [0-9]
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096
Nota Ho bisogno tr
di rimuovere l'output newline backslash da bc. Questo numero è troppo grande per essere considerato un float o un numero di bit fisso in awk, e non voglio nemmeno creare una regexp abbastanza grande da tenere conto di tutte le cifre in sed. Piuttosto, posso capovolgerlo e inserire le virgole tra gruppi di tre cifre, quindi annullarlo:
echo 2^512 |bc -l|tr -d -c [0-9] |rev |sed -e 's/\([0-9][0-9][0-9]\)/\1,/g' |rev
13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096
awk: run time error: improper conversion(number 1) in printf("%'d
.
a="13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096"
echo "$a" | rev | sed "s#[[:digit:]]\{3\}#&,#g" | rev
13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096
sed 's/^,//g'
.
Volevo anche che la parte dopo il separatore decimale fosse correttamente separata / spaziata, quindi ho scritto questo sed-script che utilizza alcune variabili shell per adattarsi alle preferenze regionali e personali. Tiene inoltre conto delle diverse convenzioni per il numero di cifre raggruppate insieme :
#DECIMALSEP='.' # usa
DECIMALSEP=',' # europe
#THOUSSEP=',' # usa
#THOUSSEP='.' # europe
#THOUSSEP='_' # underscore
#THOUSSEP=' ' # space
THOUSSEP=' ' # thinspace
# group before decimal separator
#GROUPBEFDS=4 # china
GROUPBEFDS=3 # europe and usa
# group after decimal separator
#GROUPAFTDS=5 # used by many publications
GROUPAFTDS=3
function digitgrouping {
sed -e '
s%\([0-9'"$DECIMALSEP"']\+\)'"$THOUSSEP"'%\1__HIDETHOUSSEP__%g
:restartA ; s%\([0-9]\)\([0-9]\{'"$GROUPBEFDS"'\}\)\(['"$DECIMALSEP$THOUSSEP"']\)%\1'"$THOUSSEP"'\2\3% ; t restartA
:restartB ; s%\('"$DECIMALSEP"'\([0-9]\{'"$GROUPAFTDS"'\}\'"$THOUSSEP"'\)*\)\([0-9]\{'"$GROUPAFTDS"'\}\)\([0-9]\)%\1\3'"$THOUSSEP"'\4% ; t restartB
:restartC ; s%\([^'"$DECIMALSEP"'][0-9]\+\)\([0-9]\{'"$GROUPBEFDS"'\}\)\($\|[^0-9]\)%\1'"$THOUSSEP"'\2\3% ; t restartC
s%__HIDETHOUSSEP__%\'"$THOUSSEP"'%g'
}
Una soluzione bash
/ awk
(come richiesto) che funziona indipendentemente dalla lunghezza del numero e utilizza ,
indipendentemente thousands_sep
dall'impostazione della locale e ovunque i numeri si trovino nell'input ed evita di aggiungere il separatore dei mille dopo 1.12345
:
echo not number 123456789012345678901234567890 1234.56789 |
awk '{while (match($0, /(^|[^.0123456789])[0123456789]{4,}/))
$0 = substr($0, 1, RSTART+RLENGTH-4) "," substr($0, RSTART+RLENGTH-3)
print}'
dà:
not number 123,456,789,012,345,678,901,234,567,890 1,234.56789
Con awk
implementazioni del genere mawk
non supportano gli operatori regex di intervallo, modificare regexp in/(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/
echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")'