/ / podsumuj listę według obiektów z sumami, używając powłoki - bash, shell, unix

podsumuj listę według obiektów z sumami, używając powłoki - bash, shell, unix

Jestem nowym użytkownikiem skryptów powłoki sh. Jak mogę napisać ten skrypt pod względem sort i grep?

Tam jest skrypt sumujący wszystkie wartości listy według grupy.

awk "{ arr[$1]+=$2 }
END {
for (key in arr) printf("%st%sn", key, arr[key])
}" "$@" |
sort +0n -1

Załóżmy, że plik:

A 8
B 3
A 2
B 4

Wynik to:

A 10
B 7

Teraz mam tylko wszystko cat "$1" | sort ale jak uzyskać pojedyncze i lewe kolumny bez użycia awk?

Utrzymuję się, jak odpowiednio umieścić pierwszą kolumnę w tablicy z jej wartościami po prawej stronie.

Odpowiedzi:

3 dla odpowiedzi № 1

Zakładając, że masz bash 4.x lub wystarczająco niedawny ksh, możesz spróbować:

declare -A sum   # Use typeset -A sum in ksh, which also works in bash
cat <<"EOF" |
A 8
B 3
A 2
B 4
EOF
{
while read key value
do
((sum[$key]+=$value))
done
for key in "${!sum[@]}"
do echo "$key ${sum[$key]}"
done
} | sort

Notacja w starym stylu +0n -1 jest teraz wyrażone jako -k 1n,1 i oznacza sortowanie pierwszej kolumny (wartości klucza) numerycznie. Ponieważ najważniejsze są wartości A i B, to nie jest zbyt pomocne, więc zostawiłem kryteria sortowania. To, co byłoby dla mnie bardziej sensowne, byłoby -k 2n lub -k 2nr sortować według sumy w porządku rosnącym lub malejącym.

The bash instrukcja poleca declare -A koniec typeset -A, ale obie działają; ksh wymaga typeset -A.


0 dla odpowiedzi nr 2
FILE="input"
eval $(sed "/^$/d;s/([^ ]*) ([^ ]*)/1=$(($1 + 2))/" $FILE)
eval $(sed "/^$/d;s/ .*$//" $FILE | sort -u | sed "s/.*/echo "& = $&";/")

Pierwsze wyrażenie sed tworzy linie w postaci A=$(($A + 8)). Drugi generuje echo "A = $A"; wyrażenia.

Daje:

A = 10
B = 7

0 dla odpowiedzi № 3

Za pomocą czysty

Bez zewnętrznego narzędzia i bez widły: bardzo szybko (podczas pracy na małych plikach).

declare -A values
while read name cval;do
((values[$name]+=cval))
done

Spowoduje to zapisanie tablice asocjacyjne zawierający:

set | grep ^values=
values=([A]="10" [B]="7" )

Możesz zrzucić za pomocą:

for name in ${!values[@]};do
printf "%s: %sn" $name ${values[$name]}
done
A: 10
B: 7

Za pomocą POSIX + +

Nota: Może to być lepsze wykorzystanie for var in $(...) jak zasugerowano w ostatniej części tej odpowiedzi Kolejna odpowiedź powłoki przy użyciu tylko sed!

Gdzie używam sed zbudować dwie linie, które mają zostać przesłane bc:

sort <file |
sed "
:a;
$!N;
s/^(.*) (.*)n1 (.*)/1 2+3/;
ta;
s/^([^ ]*) ([^n]*)($|n)/"1 ";2;3/;
P;
D;
"|
bc
A 10
B 7

Pozostawienie dwóch ostatnich linii pozwala zobaczyć bc strumień:

"A ";2+8;
"B ";3+4;

Zgodny odpowiedz (używając i )

Jeśli celem jest radzenie sobie z wynikiem na Skrypt powłoki poziom:

(to zostało przetestowane pod , , i ).

for name in $(cut -d  -f1 <file | sort -u) ;do
value=0
while read cnam cval ;do
[ "$cnam" = "$name" ] && value=$((cval+value))
done <file
printf "%s: %dn" $name $value
done
A: 10
B: 7

Jak widzisz, plik wydaje się dostępny wiele razy, ale jako pamięć podręczna używa tylko Linuksa jedno przeczytanie naprawdę się pojawia (podczas pracy nad małymi plikami)!

Inne odpowiedz (używając tylko!)

Jest to zupełnie inne zastosowanie zdecydowanie analizować i łączyć pola bez poprzedni sort krok ... (był testowany pod wieloma zbyt):

for var in $(
sed -ne < file "x;G;/(.)=.*n1/!s/^(.*)n(.) (.*)$/1 2=3 /;
s/(.)=(.*)n1 (.*)/1=3+2/;x;${x;p}")
do
printf "%s: %3dn" ${var%=*} $(( ${var#*=} ))
done
A:  10
B:   7

Gdzie varname jest ${var%=*} i wartości, połączone znakiem + są w ${var#*=}:

sed -ne < file "
x;      # swap crt line and hold space
G;      # append hold space to crt line
/(.)=.*n1/ ! s/^(.*)n(.) (.*)$/1 2=3 /; # if not find, append
s/(.)=(.*)n1 (.*)/1=3+2/; # add `+` + crt value at right place
x;      # swap crt line and hold space
${   # on last line
x;    # swap crt line and hold space
p     # print
}"   # that"s all folks!
A=2+8  B=4+3