> Как ты представляешь себе это без цикла?
$ function do_x { if [ $1 -gt 0 ]; then do_x $(($1 - 1)) "$2$3" $3 ; else echo $2; fi }
$ function X { do_x $1 "" $2; }
$ X 10 "x"
xxxxxxxxxx
$ X 12 "foobar"
foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar
Ы? ;-)
Родил вот это для выравнивания строки в поле определённой длины:
str2fld () {
local str="$1"
local str_l=${#str}
local fld_l=$2
local fld_f=${3:0:1}
local fld_a=${4:0:1}
local i
if [ $str_l -eq $fld_l ]; then
echo -n "$str"
elif [ $str_l -le $fld_l ]; then
[ "$fld_a" = 'l' ] && echo -n "$str"
for ((i=0; i<$((fld_l-str_l)); i++)); do echo -n $fld_f; done
[ "$fld_a" = 'r' ] && echo -n "$str"
else
if [ "$fld_a" = 'l' ]; then
echo -n "${str:0:$fld_l}"
else
echo -n "${str:$((str_l-fld_l)):$fld_l}"
fi
fi
}
Вроде не соответствует принципу KISS, мне кажется, через $(printf ...) было бы проще (?).
О, спасибо огромное тебе, rymis, это как раз то, что и нужно было.
Сейчас переписал код функции, теперь он выглядит так:
str2fld () {
local str="$1"
local str_l=${#str}
local fld_l=$2
local fld_f=${3:0:1}
local fld_a=${4:0:1}
local l_just=
if [ $str_l -eq $fld_l ]; then
echo -n "$str"
elif [ $str_l -le $fld_l ]; then
[ "$fld_a" = 'l' ] && l_just='-'
printf "%${l_just}${fld_l}s" "$str"
else
if [ "$fld_a" = 'l' ]; then
echo -n "${str:0:$fld_l}"
else
echo -n "${str:$((str_l-fld_l)):$fld_l}"
fi
fi
}
К перечисленным недостаткам прибавлю ещё и то, что внутри выравниваемой строки тоже могут быть вхождения символа-заполнителя. М-да, поторопился я, однако.
Ребяты, меня только что осенило!
Вот, что нужно сделать, куда уж короче-то :)
str2fld_x () {
local str="$1"
local str_l=${#str}
local fld_l=$2
local fld_f=${3:0:1}
local fld_a=${4:0:1}
if [ $fld_a = 'l' ]; then
echo -n "${str}`echo -e '\t' | expand -t $fld_l | sed \"s% %${fld_f}%g\"`" | cut -b1-${fld_l}
else
echo -n "`echo -e '\t' | expand -t $fld_l | sed \"s% %${fld_f}%g\"`${str}" | rev | cut -b1-${fld_l} | rev
fi
return 0
}
Варианты с использованием Perl и PHP не принимаются: здесь даже awk был бы несколько перебором, а вы хотите вообще самостоятельные языки программирования привлечь для решения в общем-то элементарной задачи!
>а вы хотите вообще самостоятельные языки программирования привлечь для решения в общем-то элементарной задачи!
Вызов perl'ового варианта осуществляется c:
real 0m0.004s
user 0m0.000s
sys 0m0.002s
неужто так плохо?
Или неужто есть что-то заметно короче?
...
А то скоро будет как в той присказке, «Настоящий линуксоид за 3 часа напишет программу, которая за 5 секунд сделает то, что обычный пользователь руками делал бы полчаса.»
1-Й ВАРИАНТ (ДЛИННЫЙ, НО ОТНОСИТЕЛЬНО БЫСТРЫЙ)
str2fld () {
local str="$1"
local str_l=${#str}
local fld_l=$2
local fld_f=${3:0:1}
local fld_a=${4:0:1}
local i=0
local l_just=
if [ $str_l -eq $fld_l ]; then
echo -n "$str"
elif [ $str_l -le $fld_l ]; then
if [ "$fld_f" = ' ' ]; then
[ "$fld_a" = 'l' ] && l_just='-'
printf "%${l_just}${fld_l}s" "$str"
else
[ "$fld_a" = 'l' ] && echo -n "$str"
for ((; i<$((fld_l-str_l)); i++)); do echo -n $fld_f; done
[ "$fld_a" = 'r' ] && echo -n "$str"
fi
else
if [ "$fld_a" = 'l' ]; then
echo -n "${str:0:$fld_l}"
else
echo -n "${str:$((str_l-fld_l)):$fld_l}"
fi
fi
}
2-Й ВАРИАНТ (КОРОТКИЙ, НО МЕДЛЕННЫЙ):
str2fld_x () {
local str="$1"
local str_l=${#str}
local fld_l=$2
local fld_f=${3:0:1}
local fld_a=${4:0:1}
if [ $fld_a = 'l' ]; then
echo -n "${str}`echo -e '\t' | expand -t $fld_l | sed \"s% %${fld_f}%g\"`" | cut -b1-${fld_l}
else
echo -n "`echo -e '\t' | expand -t $fld_l | sed \"s% %${fld_f}%g\"`${str}" | rev | cut -b1-${fld_l} | rev
fi
return 0
}
Есть строка, положим, это
s='12345678'
Необходимо дополнить её до 10-ти символов нулями, соответственно:
N=10
f='0'
Или, если строка длиннее десяти символов, обрезать её.
Видим, что задача делится на две: строку можно пирдвинуть к левому краю фиксированного поля, а можно - к правому. И, соответственно, можно обрезать её с правого конца, а можно - с левого.
Соответственно, может быть a='l' (выравнивание по левому краю/обрезание справа) и a='r' (выравнивание по правому краю/обрезание слева)
Вот эту-то высокоинтеллектуальную задачу, не сравнимую ни с какими там,понимаешь, разложениями функций в ряд Фурье, и решает моя скромная функция в двух своих ипостасях - короткой и длинной соответственно.
Примеры вызова функции str2fld приведены ниже:
Вот вариант с добавленным выравниванием по центру (m):
str2fld () {
local str="$1"
local str_l=${#str}
local fld_l=$2
local fld_f=${3:0:1}
local fld_a=${4:0:1}
local l_just=
if [ $str_l -eq $fld_l ]; then
echo -n "$str"
elif [ $str_l -le $fld_l ]; then
if [ "$fld_f" = ' ' ]; then
[ "$fld_a" = 'l' ] && l_just='-'
printf "%${l_just}${fld_l}s" "$str"
else
if [ $fld_a = 'm' ]; then
fld_l_d2=$(((fld_l-str_l)>>1))
for ((i=0; i<$fld_l_d2; i++)); do echo -n $fld_f; done
echo -n "$str"
for ((i=0; i<$((fld_l-fld_l_d2-str_l)); i++)); do echo -n $fld_f; done
else
[ "$fld_a" = 'l' ] && echo -n "$str"
for ((i=0; i<$((fld_l-str_l)); i++)); do echo -n $fld_f; done
[ "$fld_a" = 'r' ] && echo -n "$str"
fi
fi
else
case $fld_a in
l)
echo -n "${str:0:$fld_l}"
;;
r)
echo -n "${str:$((str_l-fld_l)):$fld_l}"
;;
m)
echo -n "${str:$(((str_l-fld_l)>>1)):$fld_l}"
;;
esac
fi
}
Может проще будет сгенерить строку из символов заполнителей заведомо достаточной длины --- не менее ширины поля, хоть задать в скрипте B="XXXXXXXXXXXX", хоть с помощью A=`printf "%10s" " "`; B=${A//?/X}), а потом выводить нужное количество символов echo ${B:0:$((str_l-fld_l))} ...
На самом деле по моим наблюдениям скрипт на чистом BASH без использования вызовов сторонних программ (а printf к таким относится) без особой на то надобности - будет работать в разы быстрее. Подтверждением тому - которотенькая функция str2fld_x, которая почти в 10 раз медленнее своего длиннющего прототипа:
str2fld_x () {
local str="$1"
local fld_l=$2
local fld_f=${3:0:1}
local fld_a=${4:0:1}
if [ $fld_a = 'l' ]; then
echo -n "${str}`printf \"%.${fld_l}d\" '0' | sed \"s%0%${fld_f}%g\"`" | cut -b1-${fld_l}
else
echo -n "`printf \"%.${fld_l}d\" '0' | sed \"s%0%${fld_f}%g\"`${str}" | rev | cut -b1-${fld_l} | rev
fi
return 0
}