При попытке найти IP, которые присутствуют в обоих, можно использовать join
команда, но необходимо будет использовать sort
предварительно отсортировать файлы до присоединения к ним.
$ join -o 2.2 <(sort file1) <(sort file2)
$ join -o 2.2 <(sort file1) <(sort file2)
1.765
0.326
4.754
3.673
6.334
файл 1a:
$ cat file1a
34.123.21.32
45.231.43.21
21.34.67.98
1.2.3.4
5.6.7.8
9.10.11.12
файл 2a:
$ cat file2a
34.123.21.32 0.326 - [30/Oct/2013:06:00:06 +0200]
45.231.43.21 6.334 - [30/Oct/2013:06:00:06 +0200]
45.231.43.21 3.673 - [30/Oct/2013:06:00:06 +0200]
34.123.21.32 4.754 - [30/Oct/2013:06:00:06 +0200]
21.34.67.98 1.765 - [30/Oct/2013:06:00:06 +0200]
1.2.3.4 1.234 - [30/Oct/2013:06:00:06 +0200]
4.3.2.1 4.321 - [30/Oct/2013:06:00:06 +0200]
Выполнение join
команда:
$ join -o 2.2 <(sort file1) <(sort file2)
1.234
1.765
0.326
4.754
3.673
6.334
Примечание: Первоначальный заказ file2
потерян с этим методом, вследствие того, что мы отсортировали его сначала. Однако этот метод только должен просканировать file2
единственное время теперь, в результате.
Можно использовать grep
искать соответствия в file2
использование выравнивает, которые находятся в file1
, но этот способ не так эффективен как первый метод, который я показал Вам. Это сканирует file2
поиск каждой строки в file1
.
$ grep -f file1 file2 | awk '{print $2}'
$ grep -f file1 file2 | awk '{print $2}'
0.326
6.334
3.673
4.754
1.765
1.234
Можно убыстриться grep
производительность при помощи этой формы:
$ LC_ALL=C grep -f file1 file2 | awk '{print $2}'
Можно также сказать grep
то, что жала в file1
фиксированная длина (-F
) который также поможет в выполнении улучшения.
$ LC_ALL=C grep -Ff file1 file2 | awk '{print $2}'
Обычно в программном обеспечении, Вы стараетесь не иметь необходимость сделать этот подход, хотя, так как это - в основном цикл в типе цикла решения. Но существуют времена, когда лучше, чтобы мог быть достигнут с помощью компьютера + программное обеспечение.
Можно сказать grep
получить его шаблоны из файла с помощью -f
переключатель (который находится в стандарте POSIX):
sort file1 | uniq \ # Avoid duplicate entries in file1
| grep -f /dev/stdin file2 \ # Search in file2 for patterns piped on stdin
| awk '{print $2}' \ # Print the second field (time) for matches
> new_file # Redirect output to a new file
Обратите внимание на это, если один IP-адрес появляется многократно в file2
, все его записи времени будут распечатаны.
Это сделало задание меньше чем за 2 секунды на 5 файлах миллионлайн в моей системе.
file1
? Кроме того, Вы попробовали ответ slm?
– Joseph R.
05.11.2013, 13:31
Поскольку Вы назвали свое программирование удара вопроса, я отправлю полу пример удара.
Вы могли считать IP файл фильтра и затем проверить линию за линией и соответствовать ему против них. Но на этом объеме действительно замедляются.
Вы могли довольно легкий пузырь реализации – выбрать – вставка – сортировка слиянием и т.д., но, снова, для этого вида объема это был бы мертвый человек и скорее всего хуже затем сравнивание с методической точностью. (Во многом зависит от объема файла фильтра).
Другая опция состояла бы в том, чтобы отсортировать файл с sort
и обработайте вход, внутренний, например, двоичный поиск. Это, также, было бы намного медленнее затем другие предложения, отправленные здесь, но давайте дадим ему попытку.
Во-первых это - вопрос о версии удара. Версией 4(?) мы имеем mapfile
который читает файл для выстраивания. Это намного быстрее затем традиционное read -ra …
. Объединенный с sort
это могло быть задано сценарием чем-то как (для этой задачи):
mapfile arr <<< "$(sort -bk1,1 "$file_in")"
Затем это - вопрос о наличии алгоритма поиска для нахождения соответствий в этом массиве. Простой путь мог состоять в том, чтобы использовать двоичный поиск. Это эффективно и на, например, массив 1 000 000 элементов дал бы довольно быстрый поиск.
declare -i match_index
function in_array_bs()
{
local needle="$1"
local -i max=$arr_len
local -i min=0
local -i mid
while ((min < max)); do
(( (mid = ((min + max) >> 1)) < max )) || break
if [[ "${arr[mid]// *}" < "$needle" ]]; then
((min = mid + 1))
else
max=$mid
fi
done
if [[ "$min" == "$max" && "${arr[min]// *}" == "$needle" ]]; then
match_index=$min
return 0
fi
return 1
}
Затем Вы сказали бы:
for x in "${filter[@]}"; do
if in_array_bs "$x"; then
… # check match_index+0,+1,+2 etc. to cover duplicates.
Демонстрационный сценарий. (Не отлаженный), но просто как начинающий. Для пониженной громкости, где можно было бы хотеть только зависеть от sort
, это мог быть шаблон. Но снова s.l.o.w.e.r. b.y a. l.o.t.:
#!/bin/bash
file_in="file_data"
file_srch="file_filter"
declare -a arr # The entire data file as array.
declare -i arr_len # The length of "arr".
declare -i index # Matching index, if any.
# Time print helper function for debug.
function prnt_ts() { date +"%H:%M:%S.%N"; }
# Binary search.
function in_array_bs()
{
local needle="$1"
local -i max=$arr_len
local -i min=0
local -i mid
while ((min < max)); do
(( (mid = ((min + max) >> 1)) < max )) || break
if [[ "${arr[mid]// *}" < "$needle" ]]; then
((min = mid + 1))
else
max=$mid
fi
done
if [[ "$min" == "$max" && "${arr[min]// *}" == "$needle" ]]; then
index=$min
return 0
fi
return 1
}
# Search.
# "index" is set to matching index in "arr" by `in_array_bs()`.
re='^[^ ]+ +([^ ]+)'
function search()
{
if in_array_bs "$1"; then
while [[ "${arr[index]// *}" == "$1" ]]; do
[[ "${arr[index]}" =~ $re ]]
printf "%s\n" "${BASH_REMATCH[1]}"
((++index))
done
fi
}
sep="--------------------------------------------"
# Timestamp start
ts1=$(date +%s.%N)
# Print debug information
printf "%s\n%s MAP: %s\n%s\n" \
"$sep" "$(prnt_ts)" "$file_in" "$sep" >&2
# Read sorted file to array.
mapfile arr <<< "$(sort -bk1,1 "$file_in")"
# Print debug information.
printf "%s\n%s MAP DONE\n%s\n" \
"$sep" "$(prnt_ts)" "$sep" >&2
# Define length of array.
arr_len=${#arr[@]}
# Print time start search
printf "%s\n%s SEARCH BY INPUT: %s\n%s\n" \
"$sep" "$(prnt_ts)" "$file_srch" "$sep" >&2
# Read filter file.
re_neg_srch='^[ '$'\t'$'\n'']*$'
debug=0
while IFS=$'\n'$'\t'-" " read -r ip time trash; do
if ! [[ "$ip" =~ $re_neg_srch ]]; then
((debug)) && printf "%s\n%s SEARCH: %s\n%s\n" \
"$sep" "$(prnt_ts)" "$ip" "$sep" >&2
# Do the search
search "$ip"
fi
done < "$file_srch"
# Print time end search
printf "%s\n%s SEARCH DONE\n%s\n" \
"$sep" "$(prnt_ts)" "$sep" >&2
# Print total time
ts2=$(date +%s.%N)
echo $ts1 $ts2 | awk '{printf "TIME: %f\n", $2 - $1}' >&2
grep
и объяснил, что это - недостатки. Также добавленная дополнительная информация, которая, надо надеяться, объясняет, почему соединение + вид является самым быстрым методом. – slm♦ 05.11.2013, 15:26grep -f
достаточно умно для прохождения через файла только однажды. Я признаю ошибку. – Joseph R. 05.11.2013, 16:17