Как пометить массив в POSIX sh?

При замене внешних команд в сценарии оболочки я использовал массив, чтобы избавиться от NF awk.

Теперь, когда я перешел с bash на POSIX sh, я не могу правильно пометить массив:

#!/bin/bash
export RANGE="0 1 4 6 8 16 24 46 53"
RANGE=($RANGE)
echo arrayelements: $((${#RANGE[@]}))
LAST=$((${#RANGE[@]}-1))
echo "Last element(replace NF): ${RANGE[$LAST]}"

# ./foo
arrayelements: 9
Last element(replace NF): 53

Я использую OpenBSD, sh, и он имеет точно такой же размер, как и ksh. Изменив выше на /bin/sh, кажется, что следующее не работает:

set -A "$RANGE"
set -- "$RANGE"

Как я мог реализовать приведенный выше скрипт в /bin/sh? (Обратите внимание, что он отлично работает, если вы вызываете bash с --posix, это не то, что я ищу.)


person Charles    schedule 27.06.2011    source источник
comment
В сторону: имена переменных, написанные заглавными буквами, — плохой тон. См. соглашения об именах переменных среды на странице pubs.opengroup.org/onlinepubs/009695399/basedefs. /, четвертый абзац, имея в виду, что переменные среды и переменные оболочки имеют общее пространство имен, поэтому использование имен в нижнем регистре для ваших локальных переменных предотвратит случайную перезапись чего-либо, имеющего значение для системы.   -  person Charles Duffy    schedule 01.08.2015


Ответы (3)


Массивы не являются частью спецификации POSIX sh.

Есть и другие способы найти последний предмет. Пара возможностей:

#!/bin/sh
export RANGE="0 1 4 6 8 16 24 46 53"
for LAST_ITEM in $RANGE; do true; done
echo "Last element(replace NF): $LAST_ITEM"

or:

#!/bin/sh
export RANGE="0 1 4 6 8 16 24 46 53"
LAST_ITEM="${RANGE##* }"
echo "Last element(replace NF): $LAST_ITEM"
person Matthew Slattery    schedule 28.06.2011
comment
@AlikElzin-kilaka, если бы существующий стандарт изменился за последние 5 (сейчас 7) лет, он не был бы особенно стандартным, не так ли? - person meustrus; 14.09.2018
comment
@meustus IEEE 1003.1, как и другие стандарты, постоянно развивается и получает новые версии. В данном случае в 2017 году - за год до вашего комментария. Хотя они часто добавляют новые функции, этот не добавляет массивы в язык оболочки. - person stefanct; 30.01.2019
comment
Вы можете создавать новые стандарты, но старый стандарт никогда не заменят. Когда дело доходит до сценариев оболочки, единственный стандарт, который имеет значение, это то, что реализовано везде, что меня волнует. Для большинства людей этим стандартом является bash, а не POSIX, и даже в этом случае вы можете ожидать, что bash появится максимум 5 лет назад. Для других портативной является только оболочка Bourne 1987 года. На самом деле очень немногие оболочки даже правильно реализуют POSIX с функциями 2017 года или без них, и bash не входит в их число. - person meustrus; 05.02.2019
comment
@meustus Вопрос явно касается POSIX. Если вы считаете, что стандарт POSIX не имеет значения, лучше отметить вопрос, чтобы закрыть его. - person jinawee; 11.02.2019
comment
@jinawee, возможно, я оговорился. POSIX абсолютно актуален, особенно когда об этом прямо спрашивают. Bash и Bourne Shell являются просто примерами общих стандартов. Настоящая проблема здесь заключается в том, что в оболочках не так много места для добавления функций, требующих специального синтаксиса, потому что не так много зарезервированного синтаксиса, который уже не насыщен функциями. По сути, добавление массивов в POSIX sh, вероятно, невозможно без нарушения существующего стандарта. - person meustrus; 13.02.2019

Вы можете использовать следующий проект из Github, который реализует POSIX-совместимый массив, который работает во всех оболочках, которые я пробовал: https://github.com/makefu/array

Это не очень удобно в использовании, но я нашел, что это хорошо работает для моих целей.

person Sammy S.    schedule 05.04.2013
comment
Для тех, кому интересно, это лицензированный WTFPL PoC-скрипт, который хранит элементы массива в строке, разделенной разрывами строк. Чтобы также разрешить разрывы строк, элементы кодируются (разрывы строк заменяются на %0A, а знаки процента на %25) перед помещением их в строку и декодируются при извлечении. - person josch; 13.01.2021

Следующий код работает для меня с использованием Heirloom Bourne Shell:

#!/usr/local/bin/bournesh
# cf. Heirloom Bourne Shell, 
#     http://freshmeat.net/projects/bournesh/
#     http://www.in-ulm.de/~mascheck/bourne/

# use a caret as a pipe symbol to make sure it's a Bourne shell
# cf. http://mywiki.wooledge.org/BourneShell
ls ^ cat 1>/dev/null 2>&1 || 
   { echo 'No true Bourne shell! ... exiting ...'; exit 1; }

IFS=' '
unset RANGE
RANGE="0 1 4 6 8 16 24 46 53"
export IFS RANGE
set -- $RANGE
echo arrayelements: $#
LAST=$#
eval echo "Last element\(replace NF\): \$$#"

Обратите внимание, что IFS задается пробелом, а вокруг $RANGE нет двойных кавычек.

person ralph    schedule 28.06.2011
comment
Это работает только для элементов, которые сами по себе не содержат пробелов. Смысл наличия массива в вашей оболочке состоит в том, чтобы разрешить такие элементы. - person chepner; 09.05.2016