time
по умолчанию отправляет вывод на stderr вместо stdout. Вам просто нужно перенаправить его туда, куда вы хотите.
Вы говорите "С другой стороны, если бы я хотел time
foo, и перенаправить вывод time
, я мог бы написать, time (./foo) | bar
." но это неверно. В этом случае вывод time все равно будет отображаться на консоли, только stdout будет перенаправлен.
Вы можете перенаправить stderr специально с помощью:
(time foo) 2>&1 | bar
или все трубы с помощью:
(time foo) |& bar
Скобки необходимы, чтобы это работало правильно.
В zsh
, где основным синтаксисом для определения хэша является hash=(k1 v1 k2 v2...)
, как и в perl
(, более новые версии также поддерживают неудобный синтаксис ksh93/bash для совместимости, хотя и с вариациями, когда дело доходит до цитирования ключей)
keys=("${(@k)hash}")
values=("${(@v)hash}")
typeset -A reversed
reversed=("${(@)values:^keys}") # array zipping operator
или с помощью петли:
for k v ("${(@kv}hash}") reversed[$v]=$k
@
и двойные кавычки предназначены для сохранения пустых ключей и значений (обратите внимание, что bash
ассоциативные массивы не поддерживают пустые ключи ). Поскольку расширение элементов в ассоциативных массивах не имеет определенного порядка, если несколько элементов $hash
имеют одно и то же значение (, которое в конечном итоге станет ключом в $reversed
), вы не можете сказать, какой ключ будет использоваться. как значение в $reversed
.
Вы должны использовать флаг нижнего индекса хэша R
для получения элементов на основе значения вместо ключа, в сочетании с e
для точного соответствия (в отличие от подстановочного знака ), а затем получить ключи для этих элементов. элементы с флагом раскрытия параметра k
:
for value ("${(@u)hash}")
print -r "elements with '$value' as value: ${(@k)hash[(Re)$value]}"
zsh
(в отличие отksh93
)не поддерживает массивы массивов, но его переменные могут содержать байт NUL, поэтому вы можете использовать это для разделения элементов, если элементы иначе не содержат байты NUL, или использовать ${(q)var}
/ ${(Q)${(z)var}}
для кодирования/декодирования списка с использованием кавычек.
typeset -A seen
for k v ("${(@kv)hash}")
seen[$v]+=" ${(q)k}"
for k v ("${(@kv)seen}")
print -r "elements with '$k' as value: ${(Q@)${(z)v}}"
ksh93 была первой оболочкой, которая ввела ассоциативные массивы в 1993 году. Синтаксис присваивания значений в целом означает, что это очень сложно сделать программно, в отличие от zsh
, но, по крайней мере, в ksh93 это несколько оправдано тем, что ksh93
поддерживает сложные вложенные структуры данных.
В частности, здесь ksh93 поддерживает массивы как значения для хеш-элементов, так что вы можете сделать:
typeset -A seen
for k in "${!hash[@]}"; do
seen[${hash[$k]}]+=("$k")
done
for k in "${!seen[@]}"; do
print -r "elements with '$k' as value ${x[$k][@]}"
done
bash
десятилетия спустя добавили поддержку ассоциативных массивов, скопировали синтаксис ksh93, но не другие расширенные структуры данных,и не имеет каких-либо расширенных операторов расширения параметров zsh.
В bash
вы можете использовать цитируемый список , упомянутый в zsh, используя printf %q
или с более новыми версиями ${var@Q}
.
typeset -A seen
for k in "${!hash[@]}"; do
printf -v quoted_k %q "$k"
seen[${hash[$k]}]+=" $quoted_k"
done
for k in "${!seen[@]}"; do
eval "elements=(${seen[$k]})"
echo -E "elements with '$k' as value: ${elements[@]}"
done
Однако, как отмечалось ранее, ассоциативные массивы bash
не поддерживают пустое значение в качестве ключа, поэтому он не будет работать, если некоторые из значений $hash
пусты. Вы можете заменить пустую строку каким-нибудь заполнителем, например <EMPTY>
, или префиксом ключа с каким-нибудь символом, который вы позже удалите для отображения.
Камнем преткновения, как я уверен, вы знаете, является получение всего значения индексированного массива, когда его имя используется как значение (другой )переменной. Я не мог сделать это с меньшим, чем с промежуточным значением, значение которого становится форматом ${v[@]}
, а затем использовать для этого eval. Итак, вот этот подход:
declare -A keys
N=0 # counter for the index variables IX1, IX2, IX3,...
for key in "${!hash[@]}"; do
value="${hash[$key]}"
if [ -z "${keys[$value]}" ] ; then N=$((N+1)) ; keys[$value]=IX$N ; fi
index="${keys[$value]}" # 'index' is now name of index variable
X="\${$index[@]}"
eval "$index=( $X $key )" # adding next key to it
done
for value in "${!keys[@]}" ; do
index=${keys[$value]}
X="\${$index[@]}"
printf "Value %s is present with the following keys: %s\n" \
"$value" "$(eval echo "$X")"
done
Это для Linux bash
. Он создает индексированные массивы IX1
, IX2
и т. д. для различных встречающихся значений и сохраняет эти имена в ассоциативном массиве keys
для значений. Таким образом, ${keys[$value]}
— это имя индексированного массива, содержащего ключи для этого значения. Затем X
настраивается как переменная «фраза доступа» для набора значений, что позволяет eval echo "$X"
преобразовывать эти значения с разделением пробелами. Например, если значение имеет индексированный массив IX2
, то X
будет строкой ${IX2[@]}
.
Я считаю, что zsh
похож на то, что не поддерживает массивы массивов, поэтому, вероятно, потребуется аналогичное решение. ИМХО, фразы доступа в zsh
немного понятнее.
Вот альтернативный подход -— иметь данные в двух индексированных массивах. Один из них имеет уникальные значения, а второй может содержать повторяющиеся/повторяющиеся значения. Можно построить ассоциативный массив, в котором дублирующиеся элементы второго массива являются ключами, а соответствующие элементы первого массива — значениями, разделенными пробелом.
В приведенном ниже коде не используется eval
и используется только цикл for
код
source=("foo" "bar" "baz" "quux" "wibble" "wobble")
destination=("aa" "bb" "aa" "bb" "cc" "aa")
declare -A inverted_array
# Printout formatted arrays with headers
printf '%-10s %-20s %-30s\n' "Index" "Destination" "Source"
for ((i = ((${#source[@]} - 1)); i >= 0; i--)); do
source_i="${source["$i"]}"
destination_i="${destination["$i"]}"
printf '%-10s %-20s %-30s\n' "$i" "$destination_i" "$source_i"
tempstring="${inverted_array["$destination_i"]}"
inverted_array["$destination_i"]="$source_i"" ""$tempstring"
done
echo
printf '%-10s %-20s\n' "Key" "Value"
# Remove the last space from the every element of the resulted array and print it formatted
for index in "${!inverted_array[@]}"; do
removespace="${inverted_array[$index]}"
removespace=${removespace%" "}
inverted_array["$index"]="$removespace"
printf '%-10s %-20s\n' "$index" "${inverted_array["$index"]}"
done
echo
Выход:
Index Destination Source
5 aa wobble
4 cc wibble
3 bb quux
2 aa baz
1 bb bar
0 aa foo
Key Value
bb bar quux
aa foo baz wobble
cc wibble
П.С. Чтобы еще больше расширить/демонстрировать приведенный выше пример -, вот следующий код, который генерирует два массива. Один из них-source
содержит случайные символы длиной 5 символов, а второй-destination
содержит в качестве значений только один случайный символ 0 -9a -f.
Код для создания двух массивов индексов по 100 элементов в каждом:
for ((i = 0; i < 100; i++)); do
source+=("$(tr -dc 'a-zA-Z' </dev/urandom | head -c 5)")
destination+=("$(tr -dc '0-9a-f' </dev/urandom | head -c 1)")
done
Использование приведенного выше кода для создания ассоциативного массива приводит к следующему результату::
Key Value
9 soxRg PmUZv eOmkR cFuie wmlsO EdNdM XuloF SSfjE oHfnc FcIKE
8 hLRpa eXODM wRGkh MwZUW lfWaE WQiwU IHGjj nNEcg
7 Pdxmd ywPZQ lPQIx TKawd VTyqR
6 lIwla Docxu Dimnz ovywP HwzQv
5 ObezH tyFNS BqnWp CFlMk dDkYC
4 rNzLM GVLXH AgZSL ionEp tngzQ
3 yRfqn IdTne
2 sMSxm WKmGm ELjOL pqxqw stWnL
1 yxycd EAGRg WxBle ItLNz WUdVu shUaC qDNIO xIwdM
0 OXdHh VQcsT AFvFq sgrYK AQrjZ
f uXJor IkwDr AOGSK hYMGE PQQfu tUjbh NwrVi iqZKO hHLYU
e XhMpB TCCFr ATbxa
d ReqMh lbxFx bGivd YCGtv lAtZj
c Kvthr itbaF wIbaf LwUiB VTInv xvWbC gpyRZ
b riimt EkLbv QYpZq kgvTi tOJRH jZykW pRuMD FJVXZ xipDx wkCMN
a REJnb Xtunv raimk SemnZ xMwno EXwKi sekmg WUKhx