Как передать числовой массив из bash в csh

Во-первых, в моей защите: я использую только csh, потому что у моей группы много старых csh. Мы занимаемся научным программированием; люди-лотты, по-видимому, научились использовать csh в дни SunOS/Solaris и не сдавались несмотря на переход linux/ bash и, несмотря на очевидное превосходство IMHO последнего. Во-вторых, извиняйтесь, если это FAQ, но я не нашел ответа через SO или googling вообще, и я посвятил некоторое усилие обоим.

Сказанное:

Я хочу запустить из bash некоторые устаревшие сценарии csh, как первый шаг к [переписыванию, удалению] последнего. bash → csh отлично работает для скалярных переменных среды (envvars), поскольку я могу экспортировать их из bash и читать их из csh, как ожидалось.

Не массивы, однако... до Ответ Криса Дж. Кика ниже! Следующий пример обновляется, чтобы включить ответ Kiick, и результаты, которые он производит. Поместите следующие 2 файла в один и тот же каталог,...

array_writer.sh

#!/usr/bin/env bash
### Test writing an array, passing it to csh, and reading it there.

THIS="$0"
THIS_DIR="$(readlink -f $(dirname ${THIS}))"
THIS_FN="$(basename ${THIS})"
MESSAGE_PREFIX="${THIS_FN}:"
ERROR_PREFIX="${MESSAGE_PREFIX} ERROR:"

PARTNER_FN='array_reader.csh'
PARTNER_DIR="${THIS_DIR}"
PARTNER_FP="${PARTNER_DIR}/${PARTNER_FN}"

export YEAR='2007'
# month-related arrays for ${YEAR}
declare -a BDOM=(0 31 59 90 120 151 181 212 243 273 304 334) # 0-based-Julian of first day of each month
declare -a MDAY=(31 28 31 30 31 30 31 31 30 31 30 31) # days in each month, length=12

echo -e "${MESSAGE_PREFIX} YEAR='${YEAR}':"
# start debugging
# use subshell for IFS
( IFS=',' ; echo -e "\tBDOM=${BDOM[*]}" )
( IFS=',' ; echo -e "\tMDAY=${MDAY[*]}" )
#   end debugging

### Direct export of arrays fails, but this works!
### Note it actually exports a string: see handling in partner
echo -e "${MESSAGE_PREFIX} about to call ${PARTNER_FP}:\n"
# from /info/491912/how-to-pass-numeric-array-from-bash-to-csh/2097771#2097771
bdom=${BDOM[*]} mday=${MDAY[*]} ${PARTNER_FP}
if [[ $? -ne 0 ]] ; then
  echo -e "\n${ERROR_PREFIX} failed or not found\n"
else
  echo -e "\n${MESSAGE_PREFIX} ${PARTNER_FP} returned successfully"
fi

array_reader.csh

#!/bin/csh -f
### Test reading an array written from bash.

set THIS="$0"
# set THISDIR="$(readlink -f $(dirname ${THIS}))" # fails!
set THIS_DIRNAME=`dirname ${THIS}`
set THIS_DIR=`readlink -f ${THIS_DIRNAME}`
set THIS_FN=`basename ${THIS}`
set MESSAGE_PREFIX="${THIS_FN}:"
set ERROR_PREFIX="${MESSAGE_PREFIX} ERROR:"

if ( $?bdom ) then
  # Gotta convert passed string into a "real" csh array
  set bdom_array = ( $bdom )
  echo ${MESSAGE_PREFIX} found export=bdom, size=$#bdom_array":"
  printf "\t"          # continue on same line
  foreach item ( $bdom_array )
    printf "%d," $item # ditto
  end
  echo "" # newline to end the array-printing line
else
  echo "${ERROR_PREFIX} no export=bdom"
  exit 2
endif

echo "" # separate reports

if ( $?mday ) then
  set mday_array = ( $mday )
  echo ${MESSAGE_PREFIX} found export=mday, size=$#mday_array":"
  printf "\t"
  foreach item ( $mday_array )
    printf "%d," $item
  end
  echo "" # newline to end the array-printing line
else
  echo "${ERROR_PREFIX} no export=mday"
  exit 3
endif

exit 0

... тогда, из вашей оболочки, сделайте...

$ /path/to/array_writer.sh
array_writer.sh: YEAR='2007':
        BDOM=0,31,59,90,120,151,181,212,243,273,304,334
        MDAY=31,28,31,30,31,30,31,31,30,31,30,31
array_writer.sh: about to call /path/to/array_reader.csh:

array_reader.csh: found export=bdom, size=12:
        0,31,59,90,120,151,181,212,243,273,304,334,

array_reader.csh: found export=mday, size=12:
        31,28,31,30,31,30,31,31,30,31,30,31,

array_writer.sh: /path/to/array_reader.csh returned successfully

Ответ 1

Я не знаком с массивами в csh, но экспорт кажется достаточно простым:

в bash:

bdom=${BDOM[*]} mday=${MDAY[*]} ${PARTNER_FP}

Вам не нужна команда env, bash имеет встроенную функцию.

Чтобы сделать $bdom в список слов, вместо одной строки, используйте(). в csh:

set bdom_array = ( $bdom )

Ответ 2

bash не позволяет экспортировать массивы. ( "Тем не менее", хотя он "еще не был" в течение длительного времени.) Не значит, что проблема связана с экспортом массивов от bash до csh. Вы не можете экспортировать их с bash на bash. (И, насколько мне известно, от csh до csh.)

На самом деле не очень много обходного пути. Вы можете использовать формат printf '%q', чтобы распечатать элементы в формате, который может быть eval 'd, но вам нужно будет это делать каждый раз, когда вы изменили элемент массива, или, по крайней мере, каждый вам может потребоваться импортировать его в подоболочку. Кроме того, bash printf не обязательно экспортирует значения в формате, который будет csh eval.

Ответ 3

Как указывает @rici, bash не поддерживает экспортирующие массивы - ни с export, ни с env - и нет надежного обходного пути.

Тем не менее, , если, вы знаете, что:

  • элементы массива не содержат встроенных пространств или других символов, которым требуется экранирование
  • массив не содержит элементов, которые бывают действительными шаблонами глобирования (например, '*')

тогда вы можете сгладить свои массивы в однострочные, разделенные пробелами списки и передать их таким образом.

В вашем примере:

В array_writer.sh:

# export array as word list, i.e.:
# as single-line string with space-separated tokens
export BDOM_LIST="${BDOM[@]}"

В array_reader.csh:

# Convert word list back into array.
set BDOM=($BDOM_LIST)