Вы можете использовать следующую команду xmlstarlet :
xmlstarlet sel -t -m "/records/record" -m "*[starts-with(local-name(),field)]" -i "position()=1" -v "." --else -i "local-name() = local-name(preceding-sibling::*[1])" -v "concat(';',.)" --else -v "concat('\t',.)" -b -b -b -n input.xml
В псевдокоде это выглядит примерно так:
- For-each
/records/record
- Для каждого имени элемента, которое начинается с
field
- Если это первый элемент, выведите элемент
- Else
- Если проверьте, если текущее имя элемента совпадает с предыдущим
- Затем вывод
;Item
- Другой вывод
\tItem
b
действительно означает «разрыв» из l oop или предложение if n
выводит новую строку
Его вывод:
field1\tfield2;field2\tfield3\tfield4;field4
field1;field1\tfield3\tfield4;field4
Приведенный выше код различается на основе имен элементов. Если вместо этого вы хотите дифференцировать на основе значений элементов, используйте следующую команду:
xmlstarlet sel -t -m "/records/record" -m "*[starts-with(local-name(),field)]" -i "position()=1" -v "." --else -i ". = preceding-sibling::*[1]" -v "concat(';',.)" --else -v "concat('\t',.)" -b -b -b -n a.xml
В вашем примере XML результат такой же (поскольку имена полей и значения полей идентичны).