2017-01-17 11 views
2

Я не рад использовать eval, но все остальное, что я пробовал, не удалось из-за ограничений на синтаксис {a..b}. Вот то, что у меня есть, и да Я знаю, что я мог бы комбинировать два цикла, но eval уродливый уже.Bash: Можно ли оценить строку формата для чисел в неизменном диапазоне без использования «eval»?

cores="" 
suffixes="" 
np=$(nproc --all) 
eval 'for i in {1..'$np'} ; do cores="$cores $i"; done' 
for i in $cores ; do 
    suffixes="$suffixes "$(printf %02i $i) 
done 
+0

'for ((i = 0; i

+0

' suffixes = "$ (printf '% 02i' $ (seq $ (nproc -all)))" ' – twalberg

+1

@twalberg, ... если бы мы были готовы потребовать использование не-POSIX-инструмента который не поставляется с bash, например 'seq'. Eww. –

ответ

0
suffixes='' 
np="$(nproc --all)" 
for ((i=0; i <$np; ++i)); do suffixes="$suffixes $(printf %02i $i)"; done 
+0

'суффиксы + =' являются более короткими и эффективными. –

+0

Хотя, действительно, если бы мы заботились об эффективности, мы бы раскололи подоболочку - '$()' дорого. –

+0

@CharlesDuffy: Мы говорим о перечислении ядер процессора. Хорошая 'printf' делает все суффиксы одинаковой длины. – AlexP

7

Во-первых, агрессивно современное решение:

#!/bin/bash 
#  ^^^^- "printf -v" and C-style for loops both require bash, not /bin/sh 

np=$(nproc --all) 
cores=() 
for ((i=0; i<np; i++)); do 
    printf -v suffix '%02i' "$i" 
    cores[$i]=$suffix 
done 

Это генерирует один индексный массив: Его ключи являются основными числами, и его значения суффикс строки. Таким образом, вы можете перебрать более "${!cores[@]}", чтобы получить список номеров ядер; над "${cores[@]}", чтобы получить список строк суффикса, или используйте "${cores[$i]}" для поиска суффикса для ядра $i.


Затем раствор ближе к исходному коду, построенное для современного Баша:

#!/bin/bash 
#  ^^^^- "printf -v" and C-style for loops both require bash, not /bin/sh 

np=$(nproc --all) 
cores=""; suffixes="" 
for ((i=0; i<np; i++)); do 
    printf -v suffix '%02i' "$i" 
    cores+=" $i" 
    suffixes+=" $suffix" 
done 

Вы можете также построить только основные номера внутри массива и вычислить число суффиксов в одном шаге:

# read cores from string into an array to allow safe evaluation even with unknown IFS 
IFS=' ' read -r -a cores_arr <<<"$cores" 
# ...and expand the full array, repeating the format string for every element 
printf -v suffixes '%02i ' "${cores_arr[@]}" 

Примечательно:

  • Итерация по расширенному массиву, т.е. for i in $cores, как правило, плохая практика - если ваши ценности гарантируется только быть числовыми это может быть безопасным, но быть в курсе побочных эффектов:

    • Глоб выражения внутри строки расширяются: Если вы каким-то образом имел * в ваших данных, вы можете перебирать файлы в текущем каталоге.
    • String-splitting не допускает мелкозернистого управления границами элементов, которые делают массивы. У вас может быть array=("item one" "item two") для хранения двух предметов с пробелами в обоих именах; если вы пробовали настройки string=' "item one" "item two" ', вы получите "item как одно слово, one" в качестве второго слова и т.д.

    Следовательно, итерация по элементам массива - даже если это означает, что чтение из строки в Массив - настоятельно рекомендуется.

  • Зацикливание на произвольное количество предметов лучше всего сделать с помощью C-style for loop.

  • В приведенных выше примерах нет внешних команд, кроме nproc. Это означает, что мы не зависим от инструментов, отличных от POSIX, таких как seq.
  • Использование printf -v suffix записывает результат операций форматирования строк, выполняемых printf непосредственно в переменную с именем suffix. (Кроме того: ksh93 не имеет printf -v, но распознает использование printf внутри $() и избегает штрафа подзаголовка). См. the bash-hackers page on printf.
  • Следовательно: нет никаких подоболочек, кроме тех, которые необходимы для запуска указанной внешней команды и записи ее вывода. (Каждой подоболочке требуется fork() с другой копией оболочки после использования mkfifo(), чтобы сгенерировать FIFO для захвата его вывода, считывая этот вывод: wait() для выхода из подоболочки и т. Д., Поэтому они лучше всего сохраняются за пределами жестких циклов).

Если вам нужна совместимость с POSIX ш, напротив, мы до сих пор $(()) но не (()) (и не += работы, за исключением в контексте математики, и нет C-стиль for петли на всех). Это оставляет нас с:

#!/bin/sh 
build_suffix() { 
    np=$1; i=0 
    while [ "$i" -lt "$np" ]; do 
    printf '%02i ' "$i" 
    i=$((i+1)) 
    done 
} 
suffixes=$(build_suffix "$(nproc --all)") 

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

+0

Спасибо, Чарльз, это дает мне все идеи, которые мне нужны. Я был в восторге от синтаксиса {1..n}, который я только что обнаружил, и, несмотря на то, что в течение десятилетия писал материал оболочки, никогда не нуждался или не использовал C-style для (()), – 4dummies

0

Спасибо за вход, все. Мне было о чем подумать. Я остановился на этом, чтобы заменить на петлю и вызвать к Е (1):

np=$(nproc --all) 
suffixes=$(eval "echo {01..$np}") 

Это лаконичное и цитирование очевидно. Я все еще не в восторге от eval, но он не использует ненадежные данные, и это вносит очень (очень!) Небольшое время в общей схеме того, что я делаю.

 Смежные вопросы

  • Нет связанных вопросов^_^