У меня есть файл, созданный с помощью команды | сортировать | уникальный -c
city.txt
2 mumbaiXa
3 mumbaiXb
1 mumbaiXp
5 delhiXn
4 delhiXz
1 parisXs
7 parisXt
1 parisXa
9 parisXe
Я пытаюсь разделить на X и получить количество каждого города:
expected output:
mumbai 6
delhi 9
paris 18
Я попробовал это, но это не дало ожидаемого результата.
grep 'X' city.txt | awk '{print $2}' | awk -F 'X' '{print $1}' | sort | uniq -c
Обновлять:
Файл данных выглядит так...
1904 mumbaiXa
1167 mumbaiXa
830 mumbaiXb
565 mumbaiXp
424 delhiXn
423 delhiXz
Я дал упрощенную версию и изменил текст.
В обновлении ничего не изменилось? Приведенные ниже решения должны работать в любом случае.
нужно ли сортировать конечный результат?
Да. конечный результат должен быть отсортирован.
Вы можете избежать бесполезного grep и других связанных с ним неэффективностей, реализовав все в одном скрипте Awk.
Важным изменением здесь является замена sub
всего, что стоит после X
в ключе; но, надеюсь, сопутствующий рефакторинг также будет полезен.
awk '/X/ {
k = $2
sub(/X.*/, "", k)
a[k] += $1
}
END {
for(k in a)
print(a[k], k)
}' city.txt
Демо: https://ideone.com/8fvpgw
Если названия ваших городов могут содержать пробелы, используйте substr($0, length($1)+2)
вместо $2
.
Демо: https://ideone.com/G4ZwJR
Замена sort | uniq -c
ассоциативным массивом Awk — очень распространенная и базовая идиома; если вы все равно используете Awk, полчаса, потраченные на обучение, — это потраченное время не зря.
У меня есть файл, созданный с помощью команды | сортировать | уникальный -c
city.txt 2 mumbaiXa 3 mumbaiXb 1 mumbaiXp 5 delhiXn 4 delhiXz 1 parisXs 7 parisXt 1 parisXa 9 parisXe
Если вам разрешено вызвать команду еще раз, и она выдаст точно такой же результат, вы можете получить желаемые итоги, отбросив X и то, что находится после него, прежде чем вводить это в следующую команду, что можно сделать, например. следующий путь
command | awk 'BEGIN{FS = "X"}{print $1}' | sort | uniq -c
в противном случае, если вы хотите использовать ... | sort | uniq -c
, вам следует повторить количество раз название города, пусть city.txt
содержание будет
2 mumbaiXa
3 mumbaiXb
1 mumbaiXp
5 delhiXn
4 delhiXz
1 parisXs
7 parisXt
1 parisXa
9 parisXe
затем
awk 'sub(/X.*/,""){for(i=1;i<=$1;i+=1){print $2}}' city.txt | sort | uniq -c
дает результат
9 delhi
6 mumbai
18 paris
Объяснение: для каждой строки, где была произведена замена X с последующим нулем или более любого символа, я использую цикл for
до print
2-го поля количество раз, указанное в 1-м поле.
скопируйте и вставьте команду, содержащую sub(/X.*/,""), и это сработало. Цените вашу помощь.
Вы можете использовать Ruby для замены всей трубы:
ruby -lane 'BEGIN{cnt=Hash.new(0)}
key=$F[1][/^[^X]+/]
cnt[key]=cnt[key]+=$F[0].to_i
END{p cnt }' file
Распечатки:
{"mumbai"=>6, "delhi"=>9, "paris"=>18}
Если вы хотите аналогичное форматирование:
ruby -lane 'BEGIN{cnt=Hash.new(0)}
key=$F[1][/^[^X]+/]
cnt[key]=cnt[key]+=$F[0].to_i
END{col1=cnt.values.map(&:to_s).max_by{|k| k.length}.length
puts cnt.map{|k,v| "#{v.to_s.rjust(col1+2)} #{k}"} }' file
Распечатки:
6 mumbai
9 delhi
18 Paris
Или в порядке исходного примера и в таблице:
ruby -lane 'BEGIN{cnt=Hash.new(0)}
key=$F[1][/^[^X]+/]
cnt[key]=cnt[key]+=$F[0].to_i
END{col1=cnt.keys.map(&:to_s).max_by{|k| k.length}.length
col2=cnt.values.map(&:to_s).max_by{|v| v.length}.length
puts cnt.map{|k,v| "#{k.to_s.ljust(col1)} #{v.to_s.rjust(col2)}"} }' file
Распечатки:
mumbai 6
delhi 9
paris 18
Используя любой awk, данные, сгруппированные по городам, как ваш пример:
$ awk -F'[ X]' '$2 != prev{ if (NR>1) print tot, prev; tot=0; prev=$2} {tot+=$1} END{print tot, prev}' city.txt
6 mumbai
9 delhi
18 paris
или, если вы предпочитаете (или ваш ввод не сгруппирован, и вы по какой-то причине не хотите его сначала сортировать):
$ awk -F'[ X]' '{tot[$2]+=$1} END{for (city in tot) print tot[city], city}' city.txt
delhi 9
mumbai 6
paris 18
Название города не содержит пробелов, как Нью-Йорк.