К сожалению, msmtp не помогает вам с полем Кому :, отображаемым почтовыми клиентами. Он использует только адреса, которые вы предоставляете для связи с почтовым сервером --по какой-либо причине, он ничего не делает для включения этих адресов в тело сообщения.
This behavior is different from its handling of the From: and Date: fields, which will be automatically included by default (read more).
Вы можете увидеть это непосредственно, запустив в подробном режиме (поиск строк после " --> 354 Go ahead...
"):
echo "Subject: Test\r\n\r\nMessage contents\r\n" | msmtp -v email@example.com
echo "To: email@example.com\r\nSubject: Test\r\n\r\nMessage contents\r\n" | msmtp -v email@example.com
Как правило, почтовые серверы с радостью принимают такие сообщения --, и многие также с радостью их доставляют --, но некоторые компании с несложными технологиями ошибочно помечают все такие сообщения как спам. В этой ситуации единственным выходом может быть включение поля To:в сообщение, переданное по каналу msmtp . (Возможно, даже не обязательно указывать действующий адрес электронной почты Кому :.)
С помощью GNU awk для FPAT
и FIELDWIDTHS
(, а затем с использованием сокращения gawks \s/\S
для[[:space:]]/[^[:space:]]
):
$ cat tst.awk
BEGIN { OFS="\t" }
/^\s*$/ { next } # skip blank lines
/^\s*QSUM\s/ { # start of a new record
numRecs++
lineNr = 0
}
{
if ( (++lineNr) % 2 == 1 ) {
# tags line so find every tag and field width
FPAT = "\\S+\\s*"
$0 = $0
for (i=1; i<=NF; i++) {
tag = $i
fw = (i>1 ? fw " " : "") (i<NF ? length(tag) : "*")
gsub(/^\s+|\s+$/,"",tag)
if ( !(tag in tagNames2nrs) ) {
tagNrs2names[++numTags] = tag
tagNames2nrs[tag] = numTags
}
fldNr2tagNr[i] = tagNames2nrs[tag]
}
FPAT = ""
}
else {
# vals line so use the field widths found for tags
FIELDWIDTHS = fw
$0 = $0
for (i=1; i<=NF; i++) {
val = $i
gsub(/^\s+|\s+$/,"",val)
tagNr = fldNr2tagNr[i]
vals[numRecs,tagNr] = val
}
FIELDWIDTHS = ""
}
}
END {
for (tagNr=1; tagNr<=numTags; tagNr++) {
tag = tagNrs2names[tagNr]
printf "%s%s", tag, (tagNr<numTags ? OFS : ORS)
}
for (recNr=1; recNr<=numRecs; recNr++) {
for (tagNr=1; tagNr<=numTags; tagNr++) {
val = vals[recNr,tagNr]
printf "%s%s", val, (tagNr<numTags ? OFS : ORS)
}
}
}
.
$ awk -f tst.awk file | column -s$'\t' -t
QSUM HEADER STOCK DATE TIME TI JPS TNN LTNN PP JP AA NS CPOODE TYPE AI PNC FLAG CPO
206 CC-USER REJECT SENT TNPPP 200322 104914600 8 6 0 1 4 899599991119 12 18
206 CC-USER REJECT SENT TNPPP 200322 115844000 8 6 0 1 4 899599991555 12 18
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100 2 177 123 1 4 999999999999 1499 ORIGIN
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200 2 177 123 1 4 999999999999 1499 ORIGIN
Поскольку каждая пара строк имеет разные поля, некоторые из которых пусты, мы должны полагаться на фиксированные с полями для чтения данных, но мы не знаем, сколько полей и какой они ширины, пока не попытаемся их прочитать. Итак, этот скрипт использует FPAT для поиска каждого тега (, также известного как имя, или заголовок, или название ), в каждой строке тегов :
.TI JPS TNN LTNN PP JP
для определения ширины каждого поля (включает имя тега и последующие пробелы ), а затем может использовать FIELDWIDTHS для считывания значений из строки последующих значений:
8 6 0 1
как поля фиксированной ширины, даже если какое-либо из полей пусто.
Я не собираюсь комментировать это или иным образом объяснять это более подробно, поскольку ИМХО код довольно ясен, вы задаете МНОГО вопросов по манипулированию текстом, поэтому пришло время немного узнать об awk, прочитав код., поиск справочной страницы, добавление операторов print
, где вам нужно увидеть, какие значения имеют переменные, и т. д. Если после всего этого у вас есть конкретные вопросы, то, конечно, не стесняйтесь спрашивать.
Вышеприведенное было выполнено для этого входного файла, который совпадает с файлом в вашем вопросе, но с непоследовательными проблемами выравнивания с QSUM, STOCK, DATA,и заголовки TIME исправлены, потому что я не верю, что ваш реальный ввод может быть таким беспорядочным (, но если это так, это будет легко настроить, чтобы справиться с этим):
$ cat file
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 104914600
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991119
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 115844000
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991555
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
Я бы перевернул эту проблему с ног на голову. Начните с раздела BEGIN, создав массив из столбцов массива, который вы хотите использовать в качестве вывода , по порядку. Ширина столбца и текст (, чтобы начать с):
BEGIN {
defCol[ 1] = " 5 QSUM";
defCol[ 2] = " 28 HEADER";
...
defCol[19] = " 6 CPO";
}
Подтвердите это, написав функцию, которая просто печатает заголовок. Вы можете выполнить итерацию через defCol, например:
printf ("%.*s", 0 + defCol[j], substr (defCol[j], 5);
Затем напишите функцию, которая собирает массив входных строк между одним вводом QSUM и следующим, пропуская пустые строки. Строки только с прописными буквами и пробелами — это заголовки, строки с чем-либо еще — это данные с полями, выровненными до предыдущей строки заголовка. (Я предполагаю, что смещение верхней строки как при вводе, так и при выводе является опечаткой ).
Для каждой группы вы сопоставляете заголовок с именем столбца, чтобы определить, к какой голубиной -норе принадлежит элемент данных (в виде массива, индексируемого так же, как defCol, естественно ). Затем вы можете распечатать строку сведений, используя индексы defCol для упорядочивания полей и ширину defCol в средстве форматирования printf.
Звучит сложно, но это гибко (похоже, в ближайшем будущем у вас возникнут другие подобные проблемы )и систематически. Вы также можете помечать такие вещи, как заголовки, которые вы никогда не видели раньше, слишком длинные поля для отчета и т. д.
Более подробное функциональное описание:
Во-первых, вам нужно условие, которое определяет, когда вы завершили одну группу ввода --, которая раньше была «перерывом управления». Это могло произойти в строке, содержащей «QSUM HEADER» или что-то подобное, с некоторым шаблоном, позволяющим изменять интервалы. Вы также не хотите, чтобы это произошло в первый раз (, потому что у вас еще не было группы ), и вам нужно, чтобы это также произошло в состоянии END (, потому что у последней группы не будет заголовок, чтобы активировать его ).
Остальную часть ввода можно использовать для добавления всех частей заголовка в одну длинную строку и всех частей данных в другую длинную строку.и игнорировать все пустые строки.
Чтобы вывести группу, вы просматриваете строку заголовка, ища позиции заголовков данных, и выбираете поля в соответствующих местах в строке данных, используя функции match ()и substr (). Вы сохраняете каждый элемент данных в массиве, используя те же индексы, что и элементы заголовка.
Затем вы можете вывести поля данных точно так же, как вы делали фиксированные тексты заголовков.
Все это звучит сбивающе с толку, но все достаточно прямолинейно -. Я не могу написать код прямо сейчас, но я, вероятно, смогу опубликовать структуру позже сегодня.
На самом деле, ввод (в том виде, в котором он опубликован ), вероятно, не поддается синтаксическому анализу с какой-либо надежностью из-за изменчивости интервалов.
Например, значения в разделе ЗАПАС, ДАТА и ВРЕМЯ не всегда совпадают со значениями. Мы не можем просто считать поля, потому что в HEADER есть пробелы. Мы либо возвращаемся к двум или более пробелам в качестве разделителя, либо к некоторому «достаточно близкому» выравниванию полей. И это только пример из 40 -строк, :другие опасности ждут впереди.