bash, как выбрать три поля файла, разделенных пробелом, а затем другие поля, разделенные другим знаком? - PullRequest
0 голосов
/ 16 апреля 2019

У меня есть файл в этом формате:

aaa bbb ccc ddd eee|fff|ggg|hhh|iii|lll|mmm|nnn|ooo|ppp
aaa1 bbb1 ccc1 ddd1 eee1|fff1|ggg1|hhh1|iii1|lll1|mmm1|nnn1|ooo1|ppp1
aaa2 bbb2 ccc2 ddd2 eee2|fff2|ggg2|hhh2|iii2|lll2|mmm2|nnn2|ooo2|ppp2

Как видите, первые три поля разделены пробелом, а остальные разделены | знак. Я хотел бы выбрать первые 3 поля, а затем 8-е и 9-е поля.

Я хотел бы получить следующий вывод:

aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

Как видите, мне нужно отфильтровать по двум разделителям : пробел и труба.

Как я могу использовать bash?

Я пытался с awk, но я не смог запустить его с двумя разными разделителями.

Ответы [ 6 ]

2 голосов
/ 16 апреля 2019

Если ваш ввод может иметь трубу в первых 4 полях или пробелах в конвейерной строке, то лучше использовать этот awk, который разделяет 5-е поле, используя | в качестве разделителя:

awk 'NF>3{s = $1 OFS $2 OFS $3; sub(/^[ \t]*([^ \t]+[ \t]+){4}/, "");
if (split($0, a, "|") > 4) s = s OFS a[4] OFS a[5]; print s}' file

aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2
2 голосов
/ 16 апреля 2019

Если ваш код не настолько чувствителен к производительности, чтобы сделать awk лучшим выбором, то приведенный ниже анализ выполняет в нативном bash и делает так, чтобы получить правильные результаты, даже если поля разделены каналомкроме первого содержат пробелы:

while IFS='|' read -r -a psep_fields; do          # read into pipe-separated fields
  read -r -a space_fields <<<"${psep_fields[0]}"  # read 1st field & parse by spaces
  printf '%s %s %s %s %s\n' \
    "${space_fields[0]}" "${space_fields[1]}" "${space_fields[2]}" \
    "${psep_fields[3]}" "${psep_fields[4]}"
done

См. это работает на вашем входе в https://ideone.com/zCjpDP, возвращая в качестве вывода:

aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2
1 голос
/ 17 апреля 2019

Это будет именно то, о чем вы просили, независимо от того, содержат ли поля в разделе head (разделенные пробелами) | s, или поля в хвосте (разделенные |) пробелами.

С GNU awk для сопоставления 3-го аргумента () и \S/\s стенография:

$ cat tst.awk
match($0,/^((\S+\s+){3})(.*)/,a) {
    split(a[1],h,/\s+/)
    split(a[3],t,/[|]/)
    print h[1], h[2], h[3], t[4], t[5]
}

$ awk -f tst.awk file
aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

и с любым awk:

$ cat tst.awk
match($0,/^([^[:space:]]+[[:space:]]+){3}/) {
    split(substr($0,RSTART,RLENGTH),h,/[[:space:]]+/)
    split(substr($0,RSTART+RLENGTH),t,/[|]/)
    print h[1], h[2], h[3], t[4], t[5]
}

$ awk -f tst.awk file
aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

Выше предполагается, что вы правы, и только первые 3 поля разделены пробелами, следовательно, {3} в регулярном выражении. Если вы ошиблись и на самом деле это 4 (как кажется, это может быть в вашем опубликованном примере ввода), тогда, очевидно, просто измените {3} на {4}. Это имеет значение, только если вы хотите получить доступ к 4-му разделенному пробелами полю.

1 голос
/ 16 апреля 2019

Немного другой подход -

while read a b c d e; do
   IFS="|" read -a f <<< "$e"
   echo "$a $b $c ${f[3]} ${f[4]}"
done < input.txt
aaa bbb ccc hhh iii
aaa b|b|b ccc hhh "i i i"
aaa1 bbb1 ccc1 hhh1 iii1
aaa1 bbb1 c|c|c|1 hhh1 " i i i 1"
aaa2 bbb2 ccc2 hhh2 iii2
aaa2 bbb2 ccc2 "h h h 2" iii2

Чтение загружает поля, разбитые на обычные символы $IFS, в результате чего весь последний пакет, разделенный каналами, помещается в e.Это сохраняет любые символы канала, встроенные в a - d.Поскольку e является последней переменной, остальная часть строки сохраняется там, даже если она имеет встроенные пробелы.

e явно разбивается на каналах только в массив с именем f.Это сохраняет любые пробелы, встроенные в поля e.

Хотя это не сильно отличается от решения Чарльза ниже.

0 голосов
/ 17 апреля 2019

если ваши данные в файле 'd', попробуйте gnu awk:

awk -F'[ |]' '{print $1,$2,$3,$8,$9 } ' d
awk 'BEGIN{FPAT="\\w{3,}"}{print $1,$2,$3,$8,$9 } ' d

последнее намного лучше, так как гораздо больше контроля при поиске по полю

0 голосов
/ 17 апреля 2019

Вот одно awk решение. Слишком просто, поэтому я не уверен, какие крайние случаи мне не хватает, но я получаю желаемый результат

awk -v FS="[ |]"  '{print $1 OFS $2 OFS $3 OFS $8 OFS $9}' inputFile

результат

aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

Пояснение:

Я разделил поля регулярным выражением пробелом или трубкой [ |] и напечатал запрошенные поля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...