awk / sed / shell для объединения / объединения данных - PullRequest
3 голосов
/ 01 мая 2010

Попытка объединить некоторые данные, которые у меня есть.Входные данные будут выглядеть так:

foo bar
foo baz boo
abc def
abc ghi

И я бы хотел, чтобы выходные данные выглядели следующим образом:

foo bar baz boo
abc def ghi

У меня есть некоторые идеи использования некоторых массивов в сценарии оболочки, но яв поисках более элегантного или более быстрого решения.

Ответы [ 6 ]

3 голосов
/ 01 мая 2010

Как насчет присоединиться?

file="file"
join -a1 -a2 <(sort "$file" | sed -n 1~2p) <(sort "$file" | sed -n 2~2p)

seds там просто разбивают файл на нечетные и четные строки

2 голосов
/ 01 мая 2010

Решение awk

awk '
    {key=$1; $1=""; x[key] = x[key] $0}
    END {for (key in x) {print key x[key]}}
' filename
2 голосов
/ 01 мая 2010

Хотя ответ pixelbeat работает, я не могу сказать, что я в восторге от этого. Я думаю, что я использовал бы awk что-то вроде этого:

    { for (i=2; i<=NF; i++) { lines[$1] = lines[$1] " " $i;} }  
END { for (i in lines) printf("%s%s\n", i, lines[i]); }

Это не требует предварительной сортировки данных и должно работать нормально независимо от количества или длины полей (конечно, если не считать переполнения памяти). Единственный очевидный недостаток - вывод в произвольном порядке. Если вам нужна сортировка, вам нужно будет направить вывод через сортировку (но возврат к исходному порядку был бы чем-то другим).

0 голосов
/ 01 мая 2010

На основе чистого Bash в FGM Фрагмент :

text='
foo bar
foo baz boo
abc def
abc ghi
'

count=0
oneline=""
firstword=""
while IFS=" " read -a line ; do
   let count++
   if [[ $count -eq 1 ]]; then
      firstword="${line[0]}"
      oneline="${line[@]}"
   else
      if [[ "$firstword" == "${line[0]}" ]]; then
         unset line[0] # remove first word of line
         oneline="${oneline} ${line[@]}"
      else
         printf "%s\n" "${oneline}"
         oneline="${line[@]}"
         firstword="${line[0]}"
      fi
  fi
done <<< "$text"
0 голосов
/ 01 мая 2010

Pure Bash, для действительно чередующихся линий:

infile="paste.dat"

toggle=0
while read -a line ; do
  if [ $toggle -eq 0 ] ; then
    echo -n "${line[@]}"
  else
    unset line[0]               # remove first element
    echo  " ${line[@]}"
  fi
  ((toggle=1-toggle))
done < "$infile"
0 голосов
/ 01 мая 2010

Если длина первого поля фиксирована, вы можете использовать uniq с опцией -w. В противном случае вы захотите использовать awk (предупреждение: непроверенный код):

awk '
    BEGIN{last='';}
    {
        if ($1==last) {
            for (i = 1; i < NF;i++) print $i;
        } else {
            print "\n", $0;
            last = $1;
        }
    }'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...