В Bash установить двухбайтовое значение IFS

$ grep -E '^[[:blank:]]*[0-9]{4} [0-9]{4} [0-9]{4} [0-9]{4}[[:blank:]]*$' file
4556 4618 7843 8732
 4532 0861 1932 5122
6011 2966 7184 4668
 4485 0721 1308 2759

Ваше выражение соответствует строкам с четырьмя или более наборами пробелов, -разделенных четырьмя -цифрами. Скобки ничего не делают в выражении.

Вышеприведенное выражение привязывает шаблон к началу и концу строки и позволяет использовать только пробелы или символы табуляции перед первым и после последнего наборов цифр.

В качестве альтернативы якорям ^и $можно использоватьgrep -x:

grep -Ex '[[:blank:]]*[0-9]{4} [0-9]{4} [0-9]{4} [0-9]{4}[[:blank:]]*'

И, сокращая это, точно так же, как Джефф показал ,

grep -Ex '[[:blank:]]*([0-9]{4} ){3}[0-9]{4}[[:blank:]]*'
3
18.10.2020, 20:09
3 ответа

IFSпредставляет собой набор одиночных разделителей символов -, поэтому с IFS=,;любой из ;или ,будет работать как разделитель, а a,b,;c;dбудет иметь пять полей. Если вы хотите использовать только комбинацию ,;в качестве отдельного разделителя, вам придется сделать это вручную. Один из способов - заменить эту пару ,;каким-то одним символом, который вы затем вставите вIFS:

s='a,b,;c;d'
IFS=#
fields=(${s//,;/#})

${s//,;/#}заменяет все подстроки ,;на #, а затем расширение без кавычек разделяет результат. Теперь массив fieldsсодержит a,bи c;d. Обратите внимание, что он также будет использовать полученные слова в качестве универсальных символов (имен файлов ). Вы можете предотвратить это с помощью set -f/ set -o noglob, но обратите внимание, что назначение IFSимеет глобальный эффект.

Или вы можете использовать sed, особенно если у вас есть труба для начала:

sed -e 's/,;/#/g'
1
18.03.2021, 22:56

Функция bash (версия 4.3+ ):

split() {
    local string=$1 fs=$2
    local -n fields=$3
    fields=()
    while [[ $string =~ (.*)"$fs"(.*) ]]; do
        fields=( "${BASH_REMATCH[2]}" "${fields[@]}" )
        string=${BASH_REMATCH[1]}
    done
    fields=( "$string" "${fields[@]}" )
}

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

$ string="field1,;field2,field2b,;field3a;field3b,;,;field4"
$ split "$string" ",;" result
$ declare -p result
declare -a result=([0]="field1" [1]="field2,field2b" [2]="field3a;field3b" [3]="" [4]="field4")

Он потерпит неудачу, как и многие наивные попытки реализовать синтаксический анализ CSV, с разделителем, заключенным в кавычки:

$ split 'Thoughtfully, he said "Hello, friend."', x
$ declare -p x
declare -a x=([0]="Thoughtfully" [1]=" he said \"Hello" [2]=" friend.\"")
2
18.03.2021, 22:56

Вы можете переключиться на zshвместо bashи использовать егоs(для split )флаг расширения параметра:

$ string='foo,;bar,;,;baz'
$ words=("${(@s[,;])string}")
$ typeset -p words
typeset -a words=( foo bar '' baz )

Обратите внимание, что это разбиение , а не разграничение , foo,;будет разбито на fooи пустую строку, а не только foo, как bashразделение IFS будет (только с одним символом ).

Также обратите внимание, что вbashzsh, но не во всех оболочках ), разбиение слов выполняется по символам из $IFS, а не по байтам . Например, с IFS='é'Stéphaneбудет разделен на Stи phaneдаже в локалях, где éзакодировано двумя байтами (, как в тех локалях, где чармап — UTF -8, самый распространенный в наши дни ).

2
18.03.2021, 22:56

Теги

Похожие вопросы