Как сопоставить шаблон на основе значения поля в файле и заменить его? - PullRequest
1 голос
/ 25 мая 2019

У меня есть текстовый файл следующего формата:

label1-label1/label2 label1
label3-label3/label4 label3
label5-label5/label6 label5

Я хочу сгенерировать вывод в следующем формате:

label1/label2
label3/label4
label5/label6

Я не могу использовать '-' или'/' в качестве разделителей, поскольку они также являются частью метки.

Я пытался использовать команды awk и sed для извлечения $1 и $2 из файла, но не смогчтобы выяснить, как предоставить $2 в качестве исходного шаблона для замены из $1.

Я думал об использовании $2 (label1) для сопоставления с шаблоном в $1 (label1-label1 / label2) и обрезать его, чтобы получить результат, label1 / label2, но не знал, как это сделать.

Пример: Метка 1: США, Метка 2: Нью-Йорк

Ввод: -

United-States-United-States/New-York United-States

Выход: -

United-States/New-York

Ответы [ 3 ]

0 голосов
/ 25 мая 2019

Великие умы думают одинаково ... Хотя вы можете использовать awk разделение, вероятно, столь же легко использовать цикл while для чтения информации из вашего файла и разделения первой комбинации нужных данных.в label1 и label2 с использованием простых расширений параметров обрезки от <-end и затем beginning-> до символа '/', который разделяет их.В этот момент вам просто нужно взять 1/2 length + 1 из label1 и затем объединить его с сохраненным label2 для желаемой строки.

Что-то похожее на:

while read -r label stuff; do   ## read combined label, ignore 2nd string stuff
    l1="${label%/*}"            ## isolate label1 (l1)
    l2="${label#*/}"            ## isolate label2 (l2)
    l1=${l1:$((${#l1}/2+1))}    ## take len/2+1 of l1
    echo "$l1/$l2"              ## put shortened l1 and l2 together
done < file

Пример входного файла

Использование входного файла и примера, показанного в файле:

$ cat file
label1-label1/label2 label1
label3-label3/label4 label3
label5-label5/label6 label5
United-States-United-States/New-York United-States

Пример использования / Вывод

Простой выбор и использование средней вставки мыши в терминале в каталоге, где расположен file, обеспечивает следующий желаемый вывод:

$ while read -r label stuff; do   ## read combined label, ignore 2nd string stuff
>     l1="${label%/*}"            ## isolate label1 (l1)
>     l2="${label#*/}"            ## isolate label2 (l2)
>     l1=${l1:$((${#l1}/2+1))}    ## take len/2+1 of l1
>     echo "$l1/$l2"              ## put shortened l1 and l2 together
> done < file
label1/label2
label3/label4
label5/label6
United-States/New-York

( note: UtLox был первым ответом здесь, поэтому отдавайте первенство, какой бы почтительности он ни был, это по сути тот же подход, просто другой подход к тому, где происходит длина, деление и сложение)

Вероятно, самый простой подход здесьесли вы можете гарантировать, что последняя часть строки всегда будет label1.Если это верно для вашего файла, то вам просто нужно расширение с одним параметром для вывода желаемого результата, например,

while read -r label l1; do              ## read both parts of line
    echo "$l1/${label#*/}"              ## put l1 with trimmed label
done < file

Пример использования / Вывод

$ while read -r label l1; do              ## read both parts of line
>     echo "$l1/${label#*/}"              ## put l1 with trimmed label
> done < file
label1/label2
label3/label4
label5/label6
United-States/New-York

Это просто более простой способ сделать это, если вторая часть каждой строки всегда будет вашей label1 (не нужно разбивать то, что стоит перед '/')

Дайте мне знать, если выесть дополнительные вопросы.

0 голосов
/ 02 июня 2019

Использование sed:

sed 's/^\(.*\)-\1\/\(.*\) \1$/\1\/\2/' file

Это соответствует:

  • начало строки
  • , за которым следует что-либо
  • с последующим дефисом
  • , за которым следует первое что-либо
  • , за которым следует косая черта
  • , за которым следует секунда, возможно, другая, что-нибудь
  • , за которой следует пробел
  • за ним следует первое что-нибудь
  • с последующим концом строки

и заменяет его на:

  • первое что-либо
  • , за которым следуеткосая черта
  • , за которой следует секунда что-нибудь

Линии, которые не совпадают, выводятся без изменений.

Не выводить, если ввод имеет неправильный формат:

sed '/^\(.*\)-\1\/\(.*\) \1$/!d; s//\1\/\2/' file
  • удалить строку, если неверный формат
  • в противном случае выполнить подстановку
0 голосов
/ 25 мая 2019

вот решение в чистом bash-скрипте:

#!/bin/bash
while read -r line; do
    attachment="${line##* }"                  # get last part of line
    length="${#attachment}"                   # length of last part
    printf "%s\n" "${line:length+1:-length}"  # print line, shorten the start/end by last part
done < file.txt 

ввод (file.txt)

label1-label1/label2 label1
label3-label3/label4 label3
label5-label5/label6 label5
United-States-United-States/New-York United-States
United/States/United/States/New-York United/States
United-States-United-States-New-York United-States
United$States$United$States$New$York United$States
United*States*United*States*New*York United*States

выход

label1/label2 
label3/label4 
label5/label6 
United-States/New-York 
United/States/New-York 
United-States-New-York 
United$States$New$York 
United*States*New*York 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...