$ 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:]]*'
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'
Функция 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.\"")
Вы можете переключиться на zsh
вместо bash
и использовать егоs
(для s
plit )флаг расширения параметра:
$ string='foo,;bar,;,;baz'
$ words=("${(@s[,;])string}")
$ typeset -p words
typeset -a words=( foo bar '' baz )
Обратите внимание, что это разбиение , а не разграничение , foo,;
будет разбито на foo
и пустую строку, а не только foo
, как bash
разделение IFS будет (только с одним символом ).
Также обратите внимание, что вbash
(и zsh
, но не во всех оболочках ), разбиение слов выполняется по символам из $IFS
, а не по байтам . Например, с IFS='é'
Stéphane
будет разделен на St
и phane
даже в локалях, где é
закодировано двумя байтами (, как в тех локалях, где чармап — UTF -8, самый распространенный в наши дни ).