Разбить строку на массив в сценарии оболочки

Важным флагом является флаг «выполнения» в каталоге,который в каталогах на самом деле является «проходимым» флагом. Если он не установлен, каталог нельзя использовать в пути. Таким образом, :1 )вы не можете использовать компакт-диск, а 2 )вы не можете использовать файлы в каталоге.

Давайте настроим тестовый ритг с каталогом и файлом

>>mkdir listonly
>>touch listonly/uselessfile

Пока все хорошо:

>>ls listonly/
uselessfile

Изменить исполняемый флаг каталога:

>>chmod -x listonly

Вы больше не можете сделать перезарядку:

>>cd listonly
bash: cd: listonly: Permission denied

При использовании lsвы можете видеть файлы:

>>ls listonly/
uselessfile

Однако во многих случаях lsимеет псевдоним для добавления некоторых опций, требующих просмотра метаданных для каждого файла (флаги, отметка времени, размер... ), чего он не может сделать, поскольку для этого требуется использование каталог в пути (для меня, который был ls --color=auto'), поэтому вы должны использовать обычный ls, иначе вы получите файлы, но с неприглядными ошибками:

>>ls listonly/
ls: cannot access 'listonly/uselessfile': Permission denied
uselessfile

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

2
16.06.2021, 11:34
6 ответов

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

string=11111001
echo "$string" | grep -o.

и прочитать их как массив:

readarray -t arr <<<"$(grep -o. <<<"$string")"

Тогда, конечно, каждый символ будет находиться в каждом индексе массива arr.

$ declare -p arr
declare -a arr=([0]="1" [1]="1" [2]="1" [3]="1" [4]="1" [5]="0" [6]="0" [7]="1")

Но зачем создавать новый массив, если bash может обращаться к каждому отдельному символу напрямую, как это:

$ string=11111001
echo "${string:5:1}" "${string:7:1}"
0 1

Читайте о ${parameter:offset:length}в man bash.

4
28.07.2021, 11:24
string=11111001
read -a array <<< $(echo "$string" | sed 's/./& /g')

sedдля разделения строки пробелами.

2
28.07.2021, 11:24

Более подробный способ чтения строки по одному символу за раз:

string=11111001
arr=()

while IFS= read -r -d "" -n 1 char; do
    arr+=("$char")
done < <(printf '%s' "$string")

declare -p arr

выходы

declare -a arr=([0]="1" [1]="1" [2]="1" [3]="1" [4]="1" [5]="0" [6]="0" [7]="1")
4
28.07.2021, 11:24

Для полноты, с zsh, чтобы разбить строку на:

его характер составляющие:

chars=( ${(s[])string} )

(если $stringсодержит байты, не являющиеся частями допустимых символов, каждый из них все равно будет храниться как отдельные элементы)

его байт составляющие

вы можете сделать то же самое, но предварительно сняв многобайтовую опцию, например, локально в анонимной функции:

(){ set -o localoptions +o multibyte
  bytes=( ${(s[])string} )
}

его графемный кластер составляющих.

Вы можете использовать возможности PCRE для их сопоставления с\X:

zmodload zsh/pcre
(){
  graphemes=()
  local rest=$string match
  pcre_compile -s '(\X)\K.*'
  while pcre_match -v rest -- "$rest"; do
    graphemes+=($match[1])
  done
}

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


С string=$'Ste\u0301phane'те дают:

chars=( S t e ́ p h a n e )
bytes=( S t e $'\M-L' $'\M-\C-A' p h a n e )
graphemes=( S t é p h a n e )

Поскольку графемный кластер e+ U+0301 (, который устройства отображения обычно представляют так же, как и предварительно составленный эквивалент éU+00E9, )состоит из 2 символов (U+0065 и U+0301 ), где в локалях, использующих UTF -8 в качестве своего шармапа, первый кодируется одним байтом (0x65 ), а второй — двумя байтами (0xcc 0x81, также известный как Мета -L и Мета -Ctrl -A ).

Для строк, состоящих только из символов ASCII, таких как 11111001, все три будут эквивалентны.

Обратите внимание, что в zsh, как и во всех других оболочках, кроме ksh/bash, индексы массива начинаются с 1, а не с 0.

5
28.07.2021, 11:24

В bash4.4+, поскольку bashв любом случае не может хранить символы NUL в своих переменных, вы можете вызвать другую утилиту, чтобы выполнить разбиение и распечатать результат NUL -с разделителями, который вы можете прочитать в массив с readarray -td ''.

Если ваша система поставляется с реализацией GNU grep, вы можете сделать:

readarray -td '' bytes < <(printf %s "$string" | LC_ALL=C grep -zo.)
readarray -td '' chars < <(printf %s "$string" | grep -zo.)
readarray -td '' graphemes < <(printf %s "$string" | grep -zPo '\X')

Все, кроме первого, будут пропускать байты, которые не являются частью допустимых символов в локали (по крайней мере с GNU grep3.4 ). Например, сstring=$'Ste\u0301phane \\\xf0\x80z.'(замыкающая часть не образует действительного UTF -8 )в локали UTF -8, что дает:

declare -a bytes=([0]="S" [1]="t" [2]="e" [3]=$'\314' [4]=$'\201' [5]="p" [6]="h" [7]="a" [8]="n" [9]="e" [10]=" " [11]="\\" [12]=$'\360' [13]=$'\200' [14]="z" [15]=".")
declare -a chars=([0]="S" [1]="t" [2]="e" [3]="́" [4]="p" [5]="h" [6]="a" [7]="n" [8]="e" [9]=" " [10]="\\" [11]="z" [12]=".")
declare -a graphemes=([0]="S" [1]="t" [2]="é" [3]="p" [4]="h" [5]="a" [6]="n" [7]="e" [8]=" " [9]="\\" [10]="z" [11]=".")

Если не в системе GNU и предполагается, что $stringсодержит допустимый текст UTF -8, вместо этого можно использовать perl:

readarray -td '' bytes < <(perl -0le 'print for split "", shift' -- "$string")
readarray -td '' chars < <(perl -CSA -0le 'print for split "", shift' -- "$string")
readarray -td '' graphemes < <(perl -CSA -0le 'print for shift =~ /\X/g' -- "$string")
3
28.07.2021, 11:24

Использование Raku (, ранее известного как Perl _6):

~$ OLDIFS="$IFS"
~$ IFS=" "
~$ string=11111001
~$ read -a array <<< "$(raku -e lines.comb.print <<<"$string")"
~$ declare -p array
declare -a array='([0]="1" [1]="1" [2]="1" [3]="1" [4]="1" [5]="0" [6]="0" [7]="1")'
~$ IFS="$OLDIFS"
~$ echo -n "$IFS" | raku -e 'dd($*IN.slurp);'
" \t\n"

Юникод в Раку:
Согласно документам, «Raku применяет нормализацию по умолчанию ко всему вводу и выводу, за исключением имен файлов, которые читаются и записываются как UTF8 -C8; графемы, которые являются пользовательскими -видимыми формами символов, будет использовать нормализованное представление." Таким образом, приведенный ниже код/символы дают следующие результаты:

~$ OLDIFS="$IFS"
~$ IFS=" "
~$ string1="palmarés,Würdigung,Témoignages d'honneur"
~$ read -a array1a <<< "$(raku -e lines.subst\(/"\s"/,「_」\).split\(「,」\).print <<<"$string1")"
~$ echo "${array1a[@]}"
palmarés Würdigung Témoignages_d'honneur
~$ declare -p array1a
declare -a array1a='([0]="palmarés" [1]="Würdigung" [2]="Témoignages_d'\''honneur")'

~$ read -a array1b <<< "$(raku -e lines.comb.print <<<"${array1a[2]}")"
~$ echo "${array1b[@]}"
T é m o i g n a g e s _ d ' h o n n e u r
~$ declare -p array1b
declare -a array1b='([0]="T" [1]="é" [2]="m" [3]="o" [4]="i" [5]="g" [6]="n" [7]="a" [8]="g" [9]="e" [10]="s" [11]="_" [12]="d" [13]="'\''" [14]="h" [15]="o" [16]="n" [17]="n" [18]="e" [19]="u" [20]="r")'
~$ IFS="$OLDIFS"
~$ echo -n "$IFS" | raku -e 'dd($*IN.slurp);'
" \t\n"

https://docs.raku.org/language/unicode#Normalization
https://github.com/MoarVM/MoarVM/blob/master/docs/strings.asciidoc
[код протестирован на:GNU bash, версия 3.2.57 (1 )-выпуск (x86 _64 -яблоко -darwin14 ]

РЕДАКТИРОВАТЬ _1:«Правильный» способ обработки строк со встроенными переводами строк, по-видимому, состоит в том, чтобы проглотить строку вместо чтения с флагом командной строки Raku ne, т.е. raku -e slurp.comb.printвместо raku -ne.comb.print. Затем $IFSможно настроить для создания массива с использованием (или игнорированием )новых строк.

РЕДАКТИРОВАТЬ _2:Как отметили @StephaneChazelas и @roaima, звездочки(*с )проблематичны из-за подстановки файла -. Вот код, показывающий, что цитата здесь (и выше )является правильной:

~$ string_star="*11111001"
~$ echo "$string_star"
*11111001
~$ read -a array_star <<< "$(raku -e slurp.comb.print <<<"$string_star")"
~$ echo "${array_star[@]}"
* 1 1 1 1 1 0 0 1

Двойное -цитирование необходимо (выше ), однако в качестве дополнительной меры Раку можно использовать для удаления всех *путем добавления вызова, такого как .subst(...), (здесь заменяя ничем ). Работа -в коде выполнения -ниже (рассмотрите возможность применения того же подхода для удаления других специальных символов в bash, таких как \, [и?):

~$ read string_nostar <<< "$(raku -e slurp.subst\(「*」\).print <<<"$string_star")"
~$ read -a array_nostar <<< "$(raku -e slurp.comb.print <<<"$string_nostar")"
~$ echo "${array_nostar[@]}"
1 1 1 1 1 0 0 1
0
28.07.2021, 11:24

Теги

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