Я пытаюсь найти решение дляэтотвопрос. Мой подход к этой проблеме на данный момент следующий.
- Объедините все символы вместе, чтобы получилась длинная строка.
- После выполнения вышеуказанного шага удалите все пробелы и символы табуляции, чтобы у нас осталась одна большая строка.
Мне удалось выполнить вышеуказанные шаги с помощью следующей команды.
column -s '\t' inputfile | tr -d '[:space:]'
Итак, для такого входного файла,
1 0 0 0 0 0
0 1 1 1 0 0
После применения вышеуказанной команды у меня будут такие значения:
100000011100
Теперь в этой большой строке я пытаюсь применить подход, описанный ниже.
Извлечь каждый 6 -й символ (как того требует исходный OP) и добавить его к элементу массива до конца строки.
Итак, по сути, с помощью вышеуказанного шага я пытаюсь создать элементы массива следующим образом:
10
(1 -й и 7 -й символ), 01
(2 -й и 8 -й символ), 01
(3 -й и 9 -й символ), 01
(4 -й и 10 -й символ), 00
(5 -й и 11 -й символ), 00
(6- й и 12 -й символ).
Итак, мой вопрос: как мне извлечь каждый n- й символ, чтобы добавить их в массив и продолжить работу? (в данном случае n=6).
решение1
Две линии
Вот чистое 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
, через ${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
решение2
С использованием 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
решение3
Как решение оболочки, getopts
вероятно, самое простое. Дело в getopts
том, что POSIX-определено делать именно то, что вы просите — обрабатывать поток байтов в цикле оболочки. Я знаю, это звучит странно, потому что, если вы похожи на меня до того, как я узнал об этом, вы, вероятно, думаете:ну, черт возьми, я думал, что он должен обрабатывать параметры командной строки.Это правда, но и первое тоже верно. Подумайте:
-thisisonelongstringconsistingofseparatecommandlineswitches
Да, getopts
должен справиться с этим. Он должен разделить этот символ за символом в цикле и вернуть вам каждый символ либо в переменной оболочки $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
Хм... Интересно, сработало ли это?
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
все равно помещает текущее значение opt char в, $OPTARG
даже если он возвращает вам ошибку(представлено знаком ?, назначенным указанной вами переменной opt)и в противном случае явно сбрасывает, $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
after way. Затем, когда я готов, я могу взять всю строку, и когда я это делаю, я обычно удаляю первый байт.
решение4
sed
— это первое, что приходит мне в голову.
$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz
Соберите 5 персонажей, поймайте шестого и замените их всех этим пойманным персонажем.
Однако это может привести к проблеме, если длина строки не кратна 6:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g'
6bhntuvwxy
sed
Но мы можем это исправить , немного изменив :
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt
Из-за жадной природы регулярных выражений совпадения переменной длины будут соответствовать максимально возможному количеству символов, и если для захвата ничего не осталось, то захват не выполняется, а символы просто удаляются.