Как я могу заставить XMLStarlet извлекать несколько ролей разработчика при анализе pom.xml?

Я разбираю сотни poms с помощью скрипта bash в странной иерархии, чтобы извлечь обзор всех проектов в один отчет (что-то вроде того, что maven-info-projects:project-team может' сделать за один раз). По неизвестным причинам я не хочу связываться с родительским pom или пытаться настроить разделы maven-info-projects.

Я использую XMLStarlet, поскольку он установлен, и xmlint — нет.

Учитывая экстракт pom.xml, который содержит:

<developer>
   <id>devId</id>
   <name>Developer Name</name>
   <email>[email protected]</email>
   <roles>
      <role>Project manager</role>
      <role>Developer</role>
   </roles>
</developer>

Как извлечь всю информацию о разработчике, включая несколько ролей, одним вызовом XMLStarlet?

На данный момент я могу извлечь большую часть своей информации с помощью:

# Developers
locate_section_values $pom_file_name "/x:project/x:developers/x:developer" \
    "concat( \
        x:id, '|', x:name, '|', x:email, '|', x:roles, '|', \
        x:organization, '|', x:organizationUrl, '|', x:timezone
     )"

куда

function locate_section_values(){
  local xml_file=$1
  local section=$2
  local value_table=$3

  OLD_IFS=$IFS
  IFS=$'\n'
  xml_values=()
  xml_values=(`xmlstarlet sel -B -N x="http://maven.apache.org/POM/4.0.0" -t -m "$section" -v "$value_table" -nl $xml_file`)
  IFS=$OLD_IFS
}

Затем я разделил результаты:

  for developer in ${xml_values[@]}; do
    IFS='|' 
    set $developer # split into $1, $2, etc using | as seperator
    #echo "id:${1}, name:${2}, roles:${4}"

    if [ -n "${1}" ]; then # id
      developer_id=${1}
      developer_ids+=( $developer_id )
    fi
    ...

Проблема в том, что разработчик с несколькими ролями объединяет свои роли:

 Project managerDeveloper

Есть ли способ указать исходному вызову xmlstarlet объединить несколько ролей, например, в список, разделенный запятыми?


person KevinM    schedule 28.01.2013    source источник


Ответы (2)


Я думаю, что следующее дает примерно то, что вы хотите, но вам придется изменить интерфейс на locate_section_values:

xmlstarlet sel -T -B -N x="http://maven.apache.org/POM/4.0.0" \
   -t -m "/x:project/x:developers/x:developer" -v "x:id" -o "|" \
   -v "x:name" -o "|" -v "x:email" -o "|" \
   -m "x:roles/x:role" -v "." -o "," -b -o "|" \
   -v "x:organization" -o "|" -v "x:organizationUrl" -o "|" \
   -v "x:timezone" --nl 
  $pom_file_name

Это создает роли в виде разделенного списка с запятыми, потому что его проще кодировать.


locate_section_values без eval:

function locate_section_values() {
    local xml_file=$1 # $local_project_dir/$fixed_name/pom.xml
    local section=$2 #/x:project/x:modules/x:module
    local value_table=("${@:3}")

    OLD_IFS=$IFS
    IFS=$'\n'
    xml_values=($(xmlstarlet sel -B -N x=http://maven.apache.org/POM/4.0.0 \
        -t -m "$section" "${value_table[@]}" --nl "$xml_file"))
    IFS=$OLD_IFS
}

вызов:

locate_section_values "$pom_file_name" '/x:project/x:developers/x:developer' \
      -v 'x:id' -o '|' -v 'x:name' -o '|' -v 'x:email' -o '|' \
      -m 'x:roles/x:role' -v '.' -o ', ' -b -o '|' \
      -v 'x:organization' -o '|' -v 'x:organizationUrl' -o '|' \
      -v 'x:timezone'

зациклиться на разработчиках и извлечь поля:

for developer in "${xml_values[@]}"; do
    # get | separated fields
    IFS='|' read id name email roles org orgUrl timezone <<<"$developer"

    if [ -n "$roles" ]; then # roles
        developer_roles_csv=${roles%, } # strip trailing comma
    fi

    echo "$name ($id) has roles: $developer_roles_csv."

done # developer
person npostavs    schedule 29.01.2013

Основываясь на ответе, предоставленном @npostavs, у меня есть следующее, что работает:

function locate_section_values(){
  local xml_file=$1 # $local_project_dir/$fixed_name/pom.xml
  local section=$2 #/x:project/x:modules/x:module
  local value_table=${@:3}

  OLD_IFS=$IFS
  IFS=$'\n'
  xml_values=()
  xml_cmd="xmlstarlet sel -B -N x=\"http://maven.apache.org/POM/4.0.0\" \
      -t -m \"$section\" $value_table -nl $xml_file"
  xml_values=(`eval $xml_cmd`)
  IFS=$OLD_IFS
}

Что анализирует разработчиков, если я назову это так:

  # Developers
  locate_section_values $pom_file_name "/x:project/x:developers/x:developer" \
      -v \"x:id\" -o \"\|\" -v \"x:name\" -o \"\|\" -v \"x:email\" -o \"\|\" \
      -m \"x:roles/x:role\" -v \".\" -o \", \" -b -o \"\|\" \
      -v \"x:organization\" -o \"\|\" -v \"x:organizationUrl\" -o \"\|\" \
      -v \"x:timezone\"

Я могу легко удалить завершающую «запятую и пробел» с помощью «developer_roles_csv=${4%??}»:

  OLD_IFS=$IFS
  IFS=$'\n'
  for developer in ${xml_values[@]}; do
    IFS='|' 
    set $developer # split into $1, $2, etc using | as seperator

    ....

    if [ -n "${4}" ]; then # roles
      developer_roles_csv=${4%??} # strip trailing comma
      .....
    fi

    ....

  done # developer
  IFS=$OLD_IFS
person KevinM    schedule 29.01.2013
comment
Я думаю, что определение value_table как массива позволит вам избежать eval и дополнительного уровня цитирования. - person npostavs; 29.01.2013
comment
Также: --nl или -n правильно. -nl кажется принятым, но я бы посчитал это ошибкой. - person npostavs; 29.01.2013
comment
@npostavs Мне было бы интересно увидеть вашу повторную реализацию locate_section_values с использованием value_table в качестве массива. Я действительно изо всех сил пытался заставить решение работать (мне пришлось использовать eval). - person KevinM; 29.01.2013
comment
Я добавил locate_section_values к своему ответу. Главное, что вы упустили, это то, что двойное цитирование массива ("${array[@]}") предотвращает повторное разбиение слов. - person npostavs; 30.01.2013
comment
Великолепно! Аккуратнее, легче читать и, следовательно, легче поддерживать. И это делает работу. Спасибо! - person KevinM; 30.01.2013