Первый сценарий завершается неудачно с
неожиданным токеном 'i * 1000-1000'
Что я сделал не так?
Очевидная ошибка заключается в том, что вы на самом деле не вызываете переменную $ i
в своем цикле:
for i in $(seq 10)
do
./tmp/tmpsky/bd25Par.sh (i*1000-1000) (i*1000) &
done
Это должно быть:
for i in $(seq 10)
do
./tmp/tmpsky/bd25Par.sh ($i*1000-1000) ($i*1000) &
done
Таким образом; используйте $ i
при использовании переменной. i
сам по себе работать не будет.
Что касается вычислений в оболочке, см. Комментарий номер 2.
Я думаю, вы спрашиваете о двух разных вещах.
Есть ли способ заставить bash печатать эту информацию без цикла?
Да, но они не так хороши, как просто использование цикла.
Есть ли более чистый способ получить/распечатать только часть вывода, состоящую из ключей и значений?
Да, цикл for
. Его преимущества заключаются в том, что он не требует внешних программ, прост и позволяет довольно легко контролировать точный формат вывода без сюрпризов.
Любое решение, которое пытается обработать вывод declare -p
(typeset -p
)
должен иметь дело с а) возможностью того, что сами переменные содержат круглые скобки или квадратные скобки, б) цитирование, которое declare -p
должно добавить, чтобы его вывод был действительным вводом для оболочки.
Например, ваше расширение b="${a##*(}"
съедает часть значений, если какой-либо ключ/значение содержит открывающую скобку. Это потому, что вы использовали # #
, который удаляет префикс самый длинный. То же самое для c="${b%% )*}"
. Хотя вы, конечно, могли бы более точно сопоставить шаблон, напечатанный с помощью declare
, вам все равно было бы трудно, если бы вы не хотели цитировать все, что он делает.
Это выглядит не очень красиво, если только вам это не нужно.
$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'
С помощью цикла for
проще выбрать формат вывода по своему усмотрению:
# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'
# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'
Отсюда также просто изменить формат вывода в противном случае (убрать скобки вокруг ключа поместите все пары ключ/значение в одну строку...).Если вам нужно цитировать что-то помимо самой оболочки, вам все равно придется сделать это самостоятельно, но, по крайней мере, у вас есть необработанные данные для работы. (Если у вас есть символы новой строки в ключах или значениях, вам, вероятно, понадобятся кавычки.)
С текущим Bash (я думаю, 4.4) вы также можете использовать printf "[%s]=% s" "${x@Q}" "${array[$x]@Q}"
вместо printf "%q=%q"
. Он производит несколько более приятный формат кавычек, но, конечно, немного больше работы, чтобы не забыть написать. (И он заключает в кавычки угловой регистр @
как ключ массива, который %q
не заключает в кавычки.)
Если цикл for кажется слишком утомительным для записи, сохраните его как функцию где-нибудь (без кавычек):
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
А затем просто используйте это:
$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)
Работает и с индексированными массивами:
$ b=(abba acdc)
$ printarr b
0=abba
1=acdc
declare -p array
declare -A array='([a2]="2" [a1]="1" [zz]="Hello World" [b1]="bbb" [f50]="abcd" )'
Возможно, это:
printf "%s\n" "${!array[@]}"
a2
a1
f50
zz
b1
printf "%s\n" "${array[@]}"
2
1
abcd
Hello World
bbb
printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t
a2 2
a1 1
f50 abcd
zz Hello World
b1 bbb
или это:
paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}")
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb
для сравнения с
for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb
В качестве последнего синтаксиса не использовать fork, они могли бы быть быстрее:
time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
5 11 76
real 0m0.005s
user 0m0.000s
sys 0m0.000s
time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
5 6 41
real 0m0.008s
user 0m0.000s
sys 0m0.000s
time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
5 6 41
real 0m0.002s
user 0m0.000s
sys 0m0.001s
Но это утверждение не остается верным, если массив становится большим; если сокращение вилок эффективно для небольшого процесса, использование специальных инструментов более эффективно для более крупного процесса.
for i in {a..z}{a..z}{a..z};do array[$i]=$RANDOM;done
time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
17581 35163 292941
real 0m0.150s
user 0m0.124s
sys 0m0.036s
time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
17581 17582 169875
real 0m0.140s
user 0m0.000s
sys 0m0.004s
time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
17581 17582 169875
real 0m0.312s
user 0m0.268s
sys 0m0.076s
Поскольку оба (разветвленных) решения используют выравнивание, ни одно из них не будет работать, если какая-либо переменная содержит новую строку. В этом случае единственным способом является цикл for
.
Поскольку typeset делает то, что вам нужно, почему бы просто не отредактировать вывод?
typeset -p array | sed s/^.*\(// | tr -d ")\'\"" | tr "[" "\n" | sed s/]=/' = '/
дает
a2 = 2
a1 = 1
b1 = bbb
Where
array='([a2]="2" [a1]="1" [b1]="bbb" )'
Verbose, но довольно легко увидеть, как работает форматирование: просто выполните конвейер с последовательно увеличивающимся количеством команд sed и tr. Измените их, чтобы они соответствовали вкусу печати.
Если вам нужна оболочка с лучшей поддержкой ассоциативных массивов, попробуйте zsh
.
В zsh
(где ассоциативные массивы были добавлены в 1998 г. по сравнению с 1993 г. для ksh93 и 2009 г. для bash), $var
или ${(v)var}
расширяется до (непустых) значений хеша, ${(k)var}
до (непустых) ключей (в том же порядке), и ${(kv)var}
как для ключей, так и для значений.
Чтобы сохранить пустые значения, как и для массивов, вам нужно заключить в кавычки и использовать флаг @
.
Таким образом, для вывода ключей и значений достаточно
printf '%s => %s\n' "${(@kv)var}"
Хотя для учета возможного пустого хеша вам следует сделать следующее:
(($#var)) && printf '%s => %s\n' "${(@kv)var}"
Также обратите внимание, что zsh использует гораздо более разумный и полезный синтаксис определения массива, чем ksh93
's (скопировано bash
):
typeset -A var
var=(k1 v1 k2 v2 '' empty '*' star)
Что значительно упрощает копирование или объединение ассоциативных массивов:
var2=("${(@kv)var1}")
var3+=("${(@kv)var2}")
var4=("${@kv)var4}" "${(@kv)var5}")
(вы не можете легко скопировать хэш без цикл с bash
, и обратите внимание, что bash
в настоящее время не поддерживает пустые ключи или ключи/значения с байтами NUL).
См. также zsh
функции сжатия массива, которые обычно требуются для работы с ассоциативными массивами:
keys=($(<keys.txt)) values=($(<values.txt))
hash=(${keys:^values})
Еще один вариант — перечислить все переменные и выполнить команду grep для нужной.
set | grep -e '^aa='
Я использую это для отладки. Я сомневаюсь, что это очень эффективно, поскольку в нем перечислены все переменные.
Если бы вы делали это часто, вы могли бы сделать это функцией, подобной этой:
aap() { set | grep -e "^$1="; }
К сожалению, когда мы проверяем производительность по времени:
$ time aap aa
aa=([0]="abc")
.
real 0m0.014s
user 0m0.003s
sys 0m0.006s
Поэтому, если бы вы делали это очень часто, вам бы подошла версия NO FORKS от @F.Hauri, потому что она намного быстрее.