Сценарий неправильно печатает правильные элементы массива

time по умолчанию отправляет вывод на stderr вместо stdout. Вам просто нужно перенаправить его туда, куда вы хотите.

Вы говорите "С другой стороны, если бы я хотел time foo, и перенаправить вывод time, я мог бы написать, time (./foo) | bar." но это неверно. В этом случае вывод time все равно будет отображаться на консоли, только stdout будет перенаправлен.

Вы можете перенаправить stderr специально с помощью:

(time foo) 2>&1 | bar

или все трубы с помощью:

(time foo) |& bar

Скобки необходимы, чтобы это работало правильно.

7
18.03.2019, 01:44
3 ответа

зш

поменять местами ключи <=> значения

В 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]}"

ваш perl-подход

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}}"

кш93

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>, или префиксом ключа с каким-нибудь символом, который вы позже удалите для отображения.

12
27.01.2020, 20:16

Камнем преткновения, как я уверен, вы знаете, является получение всего значения индексированного массива, когда его имя используется как значение (другой )переменной. Я не мог сделать это с меньшим, чем с промежуточным значением, значение которого становится форматом ${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немного понятнее.

3
27.01.2020, 20:16

Вот альтернативный подход -— иметь данные в двух индексированных массивах. Один из них имеет уникальные значения, а второй может содержать повторяющиеся/повторяющиеся значения. Можно построить ассоциативный массив, в котором дублирующиеся элементы второго массива являются ключами, а соответствующие элементы первого массива — значениями, разделенными пробелом.

В приведенном ниже коде не используется 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
0
29.11.2020, 15:09

Теги

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