Я сохраняю в файл несколько индивидуально сериализованных массивов PHP. Каждая строка файла содержит один сериализованный массив. Например:
a:2:{s:4:"name";s:8:"John Doe";s:3:"age";s:2:"20";}
a:2:{s:4:"name";s:8:"Jane Doe";s:3:"age";s:2:"15";}
a:2:{s:4:"name";s:12:"Steven Tyler";s:3:"age";s:2:"35";}
a:2:{s:4:"name";s:12:"Jim Morrison";s:3:"age";s:2:"25";}
a:2:{s:4:"name";s:13:"Apple Paltrow";s:3:"age";s:2:"75";}
a:2:{s:4:"name";s:12:"Drew Nickels";s:3:"age";s:2:"34";}
a:2:{s:4:"name";s:11:"Jason Proop";s:3:"age";s:2:"36";}
Вот мой вопрос:
Можно ли выполнить "awk" этот файл по следующему шаблону: "name"*"*"
Я хотел бы отсортировать найденные строки на основе содержимого второго подстановочного знака. Возможно ли это сделать с помощью awk?
Я все еще не уверен, что вы хотите, но если предположить, что интерпретация Гленна Джекмана верна, вы захотите немного расширить его идею, чтобы иметь возможность искать заданное имя поля. Например.,
awk -v FN="xxxx" -F '"' '{
i=1;
while (i<=NF-2) {
if ($i==FN) {
print $(i+2) "\t" $0;
next
} else {
i++
}
}
}' filename | sort | cut -d $'\t' -f 2-
Здесь вы должны заменить «xxxx» на «имя», «возраст» или любое другое поле, которое вы хотите использовать для сортировки.
Конечно, этот сценарий не является надежным. Поля не могут содержать символы табуляции, а также не могут содержать такие ключевые слова, как «имя», «возраст» и т. Д.
Изменить: я кратко опишу, что делает этот скрипт. По сути, awk берет заданное имя поля и для каждой строки извлекает значение этого поля. Таким образом, для каждой входной строки он выводит ту же строку, но с добавленным к ней значением этого поля и разделяет оба элемента символом табуляции. Эти выходные данные принимаются командой sort, которая сортирует их лексикографически, и, таким образом, они в основном сортируются на основе этого добавленного значения, которое является значением поля, которое вы выбрали. После сортировки таким образом это берется командой cut, которая соединяет его с символом табуляции, отбрасывая поле, которое использовалось для сортировки, и показывает только остальное (которое соответствует строкам из исходного файла, но теперь отсортировано так, как вы разыскивается).
Еще немного подробностей:
В AWK (фактически, в варианте Gawk) ключ -v определяет переменную, в данном случае с именем FN. Ключ -F определяет разделитель полей, который разделяет каждую строку, которую AWK читает из входного файла. Главный блок, определенный в фигурных скобках, - это программа AWK, которая запускается один раз для каждой строки ввода. Каждое поле строки, разделенное в соответствии с переключателем -F, ссылается на $ 1, $ 2, ..., $ (NF-1), $ NF. (NF - встроенная переменная, которая всегда равна количеству полей в текущей строке).
Как я уже сказал, AWK считывает ввод построчно и запускает эту программу для каждого из них. Например, если потребуется эта строка:
a:2:{s:4:"name";s:12:"Jim Morrison";s:3:"age";s:2:"25";}
Затем он разбивает его на двойные кавычки, например:
$1 = a:2:{s:4:
$2 = name
$3 = ;s:12:
$4 = Jim Morrison
$5 = ;s:3:
$6 = age
$7 = ;s:2:
$8 = 25
$9 = ;}
Затем сценарий перебирает каждое поле в поисках точный матч по FN. Так, если, например, мы определили FN = age, цикл остановится на 6 долларов, затем он напечатает 8 долларов (то есть, $ (6 + 2), что здесь «25»), объединенные с символом табуляции, а затем со всем сама строка ввода ($ 0). Затем будет прочитана следующая строка и весь процесс начнется заново.
Этот сценарий полагается на предположение, что ключевые слова не могут произойти где-либо еще. И это предположение нелегко обойти. Если вы хотите нарушить это предположение, необходимо больше понять, как устроен этот входной файл. Для большинства целей такое понимание достижимо, потому что эта неоднозначность также повлияет на любой анализатор сериализации. Например, если вы знаете, что имя поля (скажем, «возраст») может отображаться точно внутри других полей, но только в тех полях, которые должны быть после поле возраста, то этот сценарий в порядке, как есть. В данном примере было бы странно иметь поле имени, равное «возрасту» (например, без заглавных букв и т. Д.). В любом случае, это сложная проблема, и целые книги посвящены ей, поэтому я не буду здесь резюмировать. Погуглите "теорию компиляторов", если вам интересно.
Вы можете упомянуть одно из таких открытий: знание порядка полей. В таком случае весь этот сценарий не намного лучше, чем у Гленна. Вы можете адаптировать его более простой сценарий к каждому полю, которое захотите. Например, рассмотрите:
awk -F '"' '{print $8 "\t" $0}' filename |
sort |
cut -d $'\t' -f 2-
Этот сценарий почти идентичен тому, который предложил Гленн, только он выбирает восьмое поле («возраст») вместо четвертого («имя»).
Вид преобразования Шварца: я предполагаю, что имя всегда является четвертым полем, разделенным кавычками
awk -F '"' '{print $4 "\t" $0}' filename |
sort |
cut -d $'\t' -f 2-
Вы мог делать:
sort -t '"' -k4,4 filename
sort -t '"' -k8,8n filename
для имени и возраста соответственно, но это не позволяет вам выбирать поле по имени, а также требует утомительного подсчета полей.
В приведенном ниже скрипте представлен более надежный метод, который можно запустить одним из следующих способов:
./fieldsort "name" inputfile
some_prog | ./fieldsort "name"
Вы можете использовать «имя» или «возраст» в качестве имени поля (или другие, если они есть).
Только gawk
используется без каких-либо других утилит.
Снижается вероятность ложных срабатываний, поскольку только первая запись проверяется на предмет положения желаемого поля, и должно быть значение поля, соответствующее желаемому имени поля, появившемуся ранее в записи. Эти два условия (первое появление в первой записи) также ускоряют работу этого скрипта.
Недостатком является то, что предполагается, что все записи будут в одном формате (количество полей и т. Д.).
Проверка того, выбрано ли имя поля (хотя оно должно существовать), не выполняется, поэтому, например, «s» (тип поля «строка») будет принят, но бесполезен.
Если в командной строке указано несколько имен файлов, все они должны иметь одинаковый формат. Если вы используете Gawk 4, вы можете изменить BEGIN
к BEGINFILE
и END
к ENDFILE
(и переместите строки перед getline
и его комментарии к новому BEGIN
пункт), чтобы обойти это ограничение.
#!/usr/bin/gawk -f
func isnum(x) {
# not foolproof
return(x == x + 0)
}
BEGIN {
fieldname = ARGV[1]
delete ARGV[1]
FS = "[;:\"]"
# since gawk doesn't have a numeric sort, pad numbers
padstr = "000000000000"
# process the first line to see which field we want
# do this in the BEGIN clause to avoid repeating it for every record
getline
split($0, fields, FS)
for (f = 1; f <= length(fields); f++) {
if (fields[f] == fieldname) {
field = f + 5
break
}
}
if (field == 0) {
print "field '" fieldname "' not found in file '" FILENAME "'"
exit
}
if (isnum($field))
# pad will be null for non-numeric data
pad = substr(padstr, 1, length(padstr) - length($field))
# since we burned the first line, we need to go ahead and save it here
# the record number is included in the index to prevent losing records
# that have duplicate values in the field of interest
array[pad $field, NR] = $0
}
{
# save each of the rest of the lines in the array indexed by the field of interest
if (isnum($field))
pad = substr(padstr, 1, length(padstr) - length($field))
array[pad $field, NR] = $0
}
END {
# sort and output
c = asorti(array, indices)
for (i = 1; i <= c; i++)
print array[indices[i]]
}
Но мне интересно, почему бы вам не сделать это изначально в PHP?