Предположим, у меня есть массив с именем a
. В массиве есть 2 записи a[1]
и a[2]
.Так что каждый элемент содержит числовое значение. Оба этих значения имеют похожие начальные числа, однако у них разные окончания. Мне нужно скопировать похожую часть и проигнорировать остальное.
Так что какпример
$ echo ${a[1]}
.1.3.6.1.4.1.232.13600256
$ echo ${a[2]}
.1.3.6.1.4.1.232.13600276
Мне нужна команда для сравнения этих элементов, а затем копирования только похожей части.до первого несовпадающего поля. то есть, в этом примере
ВЫХОД
similar part is .1.3.6.1.4.1.232
Другой пример
$ echo ${a[1]}
.1.3.6.1.4.1.759.2344.454545
$ echo ${a[2]}
.1.3.6.1.4.1.759.3234.454545
ВЫХОДНЫЕ ДАННЫЕ для этого примера
similar part is .1.3.6.1.4.1.759
решение1
В sed, предполагая, что строки не содержат символов новой строки:
string1="test toast" string2="test test" printf "%s\n%s\n" "$string1" "$string2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/'
Это предполагает, что сами строки не содержат символов новой строки.
Поэтому вы можете сделать:
printf "%s\n" "${a[1]}" "${a[2]}" | sed -r 'N;s/^(.*)(\..*)?\n\1.*$/\1/'
The(\..*)
долженисключить завершающую часть .
из общей секции.
Решение состоит из двух частей:
Работаем
sed
над двумя строками. Это делается с помощьюN
, и этого можно избежать, если гарантированно нет символа во входных данных. Например, поскольку пробелы отсутствуют в элементах, как указано, мы можем вместо этого использовать:printf "%s " "${a[1]}" "${a[2]}" | sed -r 's/^(.*)(\..*)? \1.*$/\1/'
По сути, символ или строка, разделяющая два элемента в выходных данных, должны использоваться после строки
%s
форматированияprintf
и перед\1
регулярным выражением.Поиск повторяющейся строки с помощью regex. Этот трюк хорошо известен и всегда является вариацией:
(.*)\1
.*
соответствует любому набору символов и()
группирует их для дальнейшего использования по\1
. Таким образом,(.*)\1
любая последовательность символов следует сама за собой.
решение2
Вот способ на Perl. Идея состоит в том, чтобы разделить обе входные строки на отдельные массивы и перебрать массивы, сохраняя любые записи, которые идентичны в обоих:
perl -le '@A=split(//,$ARGV[0]);@B=split(//,$ARGV[1]);
for $i (0..$#A){$A[$i] eq $B[$i] ? push @S,$A[$i] : last}
print @S' "${a[0]}" "${a[1]}"
.1.3.6.1.4.1.759.
Однако это включает в себя завершающий .
. Ваш вывод не включает (несмотря на то, что он одинаков в обеих переменных), поэтому, если вы хотите удалить его, используйте это:
$ perl -le '@A=split(/\./,$ARGV[0]);@B=split(/\./,$ARGV[1]);
for $i (0..$#A){$A[$i] eq $B[$i] ? push @S,$A[$i] : last}
print join ".",@S' "${a[0]}" "${a[1]}"
.1.3.6.1.4.1.759
Объяснение
-le
: добавить новыйлine для каждого вызоваprint
и запускает скрипт, заданный-e
.@A=split(//,$ARGV[0])
:$ARGV[0]
— первый аргумент, заданный в командной строке. Это разделит ее, сделав каждый символ элементом в массиве@A
.@B=split(//,$ARGV[1]);
: то же, что и выше, но для 2-го аргумента и массива@B
.for $i (0..$#A)
: цикл for. Устанавливает$i
значение 0 и увеличивает его на единицу, пока не достигнет значения количества элементов в массиве@A
($#A
). Это простой способ перебрать все элементы в массиве, поскольку$A[$i]
будет$A[0]
,$A[1]
, ... ,$A[$#A]
.$A[$i] eq $B[$i] ? push @S,$A[$i] : last
: это сокращенная запись в стиле C. Общий формат —foo ? bar : baz
и означает «еслиfoo
истинно, то делаемbar
, иначе делаемbaz
. Здесь мы проверяем, совпадает лиn
th (или$i
th, в данном случае) элемент массива@A
с соответствующим элементом из массива@B
. Если это так, мы добавляем его в третий массив .@S
Если нет, мы выходим из цикла с помощьюlast
.print @S
: распечатать массив@S
, общие элементы.
Оба решения очень похожи, единственное отличие в том, что @A=split(/\./,$ARGV[0])
будет выполнено разделение по .
, удаление их из результирующего массива и print join ".", @S
вывод всех элементов с @S
промежутком .
между ними.
решение3
Как я уже упоминал в комментариях под вопросом, я нашел довольно простое awk
решение: объединить два числа, чтобы создать одну длинную строку, заменить все точки пробелами (чтобы разрешить использовать пробел в качестве разделителя полей по умолчанию в awk) и выполнить сравнение строк field с file+half.
Основная команда
printf ${a[1]}${a[2]} | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x };}'
Я проверил это с gawk и mawk, в обоих случаях работает.
Вот вывод для первого примера ( .1.3.6.1.4.1.232.13600256 и .1.3.6.1.4.1.232.13600276 ):
$ printf ${a[1]}${a[2]} | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x };}'
.1.3.6.1.4.1.232
Множественные сравнения
Если вы хотите сравнить несколько строк одновременно, объедините их вместе и разделите символом новой строки в printf, затем добавьте printf в конец команды awk, например так:
printf "${a[1]}${a[2]}\n${a[3]}${a[4]}" | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}'
Выход:
$ printf "${a[1]}${a[2]}\n${a[3]}${a[4]}" | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}'
.1.3.6.1.4.1.232 # same for a[1] and a[2]
.1.3.6.1.4.1.759 # same for a[3] and a[4]
Ограничение выхода
Теперь, комментарий kos'а правильно заметил, что OP хочет, чтобы отображалось только 7 чисел. Для этого вы можете добавить к cut -d'.' -f1-8
команде pipe. Вот так:
printf "${a[5]}${a[6]}" | mawk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8
Вот пример вывода с моего терминала:
$ a[5]=.1.3.6.1.4.1.232.13600256.885
$ a[6]=.1.3.6.1.4.1.232.13600256.885
$ printf "${a[5]}${a[6]}" | mawk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8
.1.3.6.1.4.1.232.13600256.885
half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8 <
.1.3.6.1.4.1.232
Упрощаем еще больше
Опять же, все можно поместить в скрипт awk.
#!/usr/bin/awk -f
{
gsub("\\."," ");
half=NF/2
};
{
for ( x=1; x<=half; x++ ) {
if ( $x==$(x + half) ) printf "."$x
};
printf "\n"
}
Пример запуска:
$ printf "${a[5]}${a[6]}" | num-comp.awk | cut -d'.' -f1-8
.1.3.6.1.4.1.232
Сравнение до первого неравного числа
В Awk есть очень полезная функция substr(string,X,Y)
, которая позволяет обрезать или «обрезать» строку от первого символа (x) до конца (Y). Итак, зная это, давайте возьмем два числа как два поля одной строки и пропустим их через цикл while. Мы будем продолжать увеличивать длину подстроки (от начала до конца), пока они не перестанут быть равными. Как только мы столкнемся с неравными подстроками, мы выйдем и выведем последнюю известную равную подстроку.
echo ".1.3.6.1.4.1.232.13600256\t.1.3.6.1.4.1.232.13600276" | awk 'BEGIN{i=1}{ while(substr($1,1,i)==substr($2,1,i)){var=substr($1,1,i);i++};} END{print var}'
Особая благодарность terdon за предложение использовать функцию substr, о существовании которой я раньше даже не подозревал.
решение4
Вы можете определить небольшую python
функцию, которая может выполнить эту работу:
#!/usr/bin/env python2
import itertools
def common_portion(a):
first = a[0].split('.')
second = a[1].split('.')
result = []
for (i, j) in itertools.izip(first, second):
if i == j:
result.append(i)
else:
break
return 'Similar part is ' + '.'.join(result)
Нам необходимо предоставить список, содержащий строки, которые мы хотим проверить, в качестве входных данных для функции.
first
переменная будет содержать части первого элемента входного списка, разделенного на.
(a[0].split
). Аналогичноsecond
будет содержать части второго элемента спискаa
.Затем мы итерируем
first
иsecond
проверяем равенство каждого элемента с его тем же индексированным аналогом, если они одинаковы, то один из них сохраняется в отдельном спискеresult
. Всякий раз, когда мы сталкиваемся с первым отличием, мы прерываем цикл.Наконец, мы напечатали желаемый результат, объединив поля с помощью
.
s ('.'.join(result)
)
Тест :
print common_portion(['.1.3.6.1.4.1.232.13600256', '.1.3.6.1.4.1.232.13600276'])
Similar part is .1.3.6.1.4.1.232
print common_portion(['.1.3.6.1.4.1.759.2344.454545', '.1.3.6.1.4.1.759.3234.454545'])
Similar part is .1.3.6.1.4.1.759