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}'
sedfunziona solo se il numero è esattamente di 9 cifre. Il printfnon funziona su zsh. Quindi la seconda sedrisposta è 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 printfsupporti praticamente tutto quello che si può fare nella printffunzione 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, --groupingpoiché le doppie sillabazioni richiedono lunghe opzioni?
--gfunziona bene per me invece di --grouping, cioè numfmt --g 1234567890e numfmt --grouping 1234567890fare 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/ AWKalternativa, quindi potrebbe non averlo usato PERLprima. In ogni caso, è meglio spiegare cosa fa il comando, specialmente per le battute.
Con alcune awkimplementazioni:
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.
awke bashavere 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 tcomando 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 trdi 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_sepdall'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 awkimplementazioni del genere mawknon supportano gli operatori regex di intervallo, modificare regexp in/(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/
echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")'