Просто сделайте:
awk 'NF != 31 || /[^0-9 -]/ {print FILENAME ":" FNR ": " $0}' file1 file2...
Сообщить о строках, которые не имеют 31 поля или имеют нецифры. Не столь строгий как решение @manatwork, поскольку это не лаяло бы на ---
или 9-8
например, но это могло бы быть более эффективно.
Вот чисто-bash
решение, которое производит массив bash
:
s="100000011100"
array=($(
for ((i=0; i<${#s}-6; i++))
do
echo "${s:$i:1}${s:$((i+6)):1}"
done
))
echo "${array[@]}"
Это производит тот же вывод, который показан в вопросе:
10 01 01 01 00 00
Ключевым элементом здесь является использование расширения подстроки bash bash. Бэш позволяет извлекать подстроки из переменной, скажем параметр
, через ${parameter:offset:length}
. В нашем случае смещение определяется переменной цикла i
, а длина всегда 1
.
Предположим, например, что наша исходная строка имеет 18 символов, и мы хотим извлечь i-ые, i+6-ые и i+12-ые символы для i от 0 до 5. Затем:
s="100000011100234567"
array=($(
for ((i=0; i<6; i++))
do
new=${s:$i:1}
for ((j=i+6; j<${#s}; j=j+6))
do
new="$new${s:$j:1}"
done
echo "$new"
done
))
echo "${array[@]}"
В результате получится:
102 013 014 015 006 007
Этот же код распространяется на произвольное количество 6-символьных строк. Например, если s
имеет три строки (18 символов):
s="100000011100234567abcdef"
Затем выдается:
102a 013b 014c 015d 006e 007f
sed
- это первое, что приходит мне в голову.
$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz
Сопоставьте 5 символов, захватите 6-й, и замените все на этот захваченный символ.
Однако, это будет иметь проблему, если длина строки не будет точно кратна 6:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g'
6bhntuvwxy
Но мы можем исправить это, немного изменив sed
:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt
Из-за жадной природы регекса, соответствия переменной длины будут совпадать настолько, насколько это возможно, и если для захвата ничего не осталось, то он не захватывает, а символы просто удаляются.
Используя perl
:
$ echo 100000011100 | perl -nle '
for ($i = 0; $i < length()/2; $i++) {
print substr($_,$i,1), substr($_,$i+6,1);
}
'
10
01
01
01
00
00
, он работает для двух строк. Если вы хотите работать с произвольным количеством строк, вам следует обрабатывать строки напрямую, вместо того, чтобы строить большую строку. С этим вводом:
1 0 0 0 0 0
0 1 1 1 0 0
0 0 0 0 0 0
Попробуйте:
$ perl -anle '
for ($i = 0; $i <= $#F; $i++) {
push @{$h{$i}}, $F[$i];
}
END {
print @{$h{$_}} for keys %h;
}
' file
000
010
000
100
010
010
В качестве решения оболочки, getopts
, вероятно, проще всего. Дело в том, что getopts
- это POSIX-специфическое решение, позволяющее делать именно то, что вы просите - обрабатывать байт-поток в цикле оболочки. Я знаю, что это звучит странно, потому что, если ты такой же, как я, до того, как я узнал это, ты, наверное, думаешь, , ну, блин, я думал, что это должно было работать с переключателями командной строки. Это правда, но это первое, что я думаю. Подумайте:
-thisisonelongstringconsistingofseparatecommandlineswitches
Да, getopts
должен справиться с этим. Он должен разделить этот символ на символ char в цикле и вернуть вам каждый символ либо в переменной shell $OPTARG
, либо в другой, которую вы указываете по имени, в зависимости от того, насколько конкретно вы получаете, когда вызываете этот символ. Более того, он должен возвращать ошибки в переменных оболочки и сохранять прогресс, когда он делает это в переменной оболочки $OPTIND
, чтобы он мог возобновить работу с того места, где он остановился, если вы можете каким-то образом адресовать его. И он должен сделать всю работу, не вызывая ни одной под-оболочки.
Скажем так:
arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done
Hmmm.... Интересно, сработало ли это?
echo "$((${#arg}/6))" "$#"
482 482
Это мило...
eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4
Итак, как вы видите, команда getopts
полностью устанавливает массив для каждого шестого байта в строке. И это не обязательно должны быть такие числа - и даже не должны быть символы, безопасные для оболочки - и вам даже не нужно указывать целевые символы, как я делал это выше с 01234565789
. Я неоднократно тестировал это во многих оболочках, и все они просто работают. Есть некоторые причуды - bash
выкинет первый символ, если это символ пробела - dash
принимает двоеточие :
в качестве заданного параметра, даже если это практически единственный POSIX, который конкретно запрещает. Но ничего из этого не имеет значения, так как getopts
все еще вносит текущее значение оптического символа в $OPTARG
, даже когда он возвращает вам ошибку (представленную ?, назначенной вашей указанной var опции), и в противном случае явно не устанавливает $OPTARG
, если только вы не объявили опцию, которая должна иметь аргумент. А пробельные символы - это вроде как хорошо - они только отбрасывают ведущий пробел, что отлично, потому что при работе с неизвестными значениями можно сделать:
getopts : o -" $unknown_value"
...чтобы запустить цикл без всякой опасности того, что первый символ на самом деле находится в вашей принятой строке аргументов - в результате чего getopts
засовывает все это в $OPTARG
сразу - в качестве аргумента.
Вот еще один пример:
OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"
do printf '\\%04o' "'$OPTARG"; done
\0040\0150\0071\0365\0320\0070\0161\0064\0274\0115\0012\0215\0222\0271\0146\0057\0166
Я установил $OPTIND=1
в первой строке, потому что я только что использовал getopts
и, пока вы его не сбросите, он ожидает, что его следующий вызов продолжится с того места, на котором он остановился - он хочет "${arg2}"
, иными словами. Но мне не хочется отдавать, и я делаю сейчас другую вещь, поэтому я даю ему знать, сбросив $OPTIND
, и в этот момент это хорошо.
В этом я использовал zsh
- который не рассуждает о ведущем пробеле - и поэтому первый символ - восьмеричный 40 - символ пробела. Обычно я не использую getopts
таким образом, хотя - я обычно использую его, чтобы избежать выполнения write()
для каждого байта и вместо этого присваивать его вывод - который приходит в переменной - другой переменной оболочки - как я делал это выше с set
после моды. Затем, когда я буду готов, я смогу взять всю строку, а когда я обычно удаляю первый байт.