Извлечь все, кроме последнего поля из переменной в bash - PullRequest
0 голосов
/ 17 октября 2018

У меня есть файл со строками, похожими на эти:

01/01 THIS IS A DESCRIPTION 123.45
12/23 SHORTER DESC 9.00
11/16 DESC 1,234.00

Три поля: дата, desc, сумма.За первым полем всегда будет пробел.Последнему полю всегда будет предшествовать пробел.Но среднее поле обычно содержит пробелы.

Я достаточно хорошо знаю bash / regex, чтобы получить первое и последнее поля (например, echo ${LINE##* } или cut -f1 -d\).Но как мне получить среднее поле?По сути, все кроме первого и последнего полей.

Ответы [ 4 ]

0 голосов
/ 17 октября 2018

bash: прочитать строку в массив слов и выбрать нужные элементы из массива

while read -ra words; do 
    date=${words[0]}
    amount=${words[-1]}
    description=${words[*]:1:${#words[@]}-2}

    printf "%s=%s\n" date "$date" desc "$description" amt "$amount"
done < file

output

date=01/01
desc=THIS IS A DESCRIPTION
amt=123.45
date=12/23
desc=SHORTER DESC
amt=9.00
date=11/16
desc=DESC
amt=1,234.00

Это забавный бит: ${words[*]:1:${#words[@]}-2}

  • взять срез массива слов из индекса 1 (2-й элемент) для длины «числа элементов минус 2»
  • слова будут объединены в одну строку с разделителем пробела.

См. Расширение параметров оболочки и прокрутите немного вниз для обсуждения ${parameter:offset:length}.


Если вы хотите использовать регулярное выражение в bash, вы можете использовать захватные скобки и массив BASH_REMATCH

while IFS= read -r line; do 
    if [[ $line =~ ([^[:blank:]]+)" "(.+)" "([^[:blank:]]+) ]]; then 
        echo "date=${BASH_REMATCH[1]}" 
        echo "desc=${BASH_REMATCH[2]}" 
        echo "amt=${BASH_REMATCH[3]}"
    fi
done < file

То же, что и выше.

Примечаниев шаблоне, где пробелы должны быть заключены в кавычки (или экранированы обратной косой чертой)

0 голосов
/ 17 октября 2018

Вы можете попробовать ниже одного с awk:

awk '{$1="";$NF="";sub(/^[ \t]*/,"")}1' file_name 
0 голосов
/ 17 октября 2018

Вы можете использовать sed для этого:

$ sed -E 's/^[^[:space:]]*[[:space:]](.*)[[:space:]][^[:space:]]*$/\1/' file
THIS IS A DESCRIPTION
SHORTER DESC
DESC

Или с awk:

$ awk '{$1=$NF=""; sub(/^[ \t]*/,"")}1' file
# same output

Вы также можете использовать cut и rev для удаления первого ипоследние поля:

$ cut -d ' ' -f2- file | rev | cut -d ' ' -f2- | rev
# same output

Или GNU grep:

$ grep -oP '^\H+\h\K(.*)(?=\h+\H+$)' file
# same output

Или с циклом Bash и расширением параметра :

$ while read -r line; do line="${line#* }"; echo "${line% *}"; done <file
# same output

Или, если вы хотите захватить поля как переменные в Bash:

while IFS= read -r line; do
    date="${line%% *}"
    amt="${line##* }"
    line="${line#* }"
    desc="${line% *}"
    printf "%5s %10s \"%s\"\n" "$date" "$amt" "$desc"
done <file    

Prints:

01/01     123.45 "THIS IS A DESCRIPTION"
12/23       9.00 "SHORTER DESC"
11/16   1,234.00 "DESC"
0 голосов
/ 17 октября 2018

Если вы хотите удалить первое и последнее поля, вы можете просто расширить метод расширения параметров, на который вы ссылались:

var=${var#* } var=${var% *}

Одна # или % удаляет самую короткую подстроку, которая соответствуетшарик.

...