Мы знаем, что это медленно от выполнения:
$ time ./junk.sh
Lines written: 14401
./junk.sh 2.27s user 3.31s system 21% cpu 25.798 total
(и это - версия, которая только печатает 4 часа, не 2 года.)
Получить лучшее понимание где bash
проводит его время, мы можем использовать strace -c
.
$ strace -c ./junk.sh
Lines written: 14401
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
79.01 0.128906 4 28806 14403 waitpid
17.92 0.029241 2 14403 clone
2.45 0.003999 0 158448 rt_sigprocmask
0.33 0.000532 0 28815 rt_sigaction
0.29 0.000479 0 14403 sigreturn
Таким образом, мы видим, что лучшие два вызова waitpid
и clone
. Они не занимают много времени самостоятельно (только 0,128906 секунды и 0,029241 секунды), но мы видим, что их называют много, таким образом, мы подозреваем, что проблемой является факт, мы должны запустить отдельное date
управляйте для повторения каждого числа.
Таким образом я сделал некоторый поиск и узнал, что можно скомпилировать bash
с gprof
поддержка путем выполнения:
$ ./configure --enable-profiling --without-bash-malloc
$ make
Теперь использование этого:
$ ./bash-gprof junk.sh
Lines written: 14401
$ gprof ./bash-gprof gmon.out
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
8.05 0.28 0.28 14403 0.00 0.00 make_child
6.61 0.51 0.23 __gconv_transform_utf8_internal
5.75 0.71 0.20 fork
5.75 0.91 0.20 259446 0.00 0.00 hash_search
5.17 1.09 0.18 129646 0.00 0.00 dispose_words
Так брать имена функций значимо, оно подтверждает, что проблема, мы делаем bash
ветвление и вызов внешняя команда неоднократно.
Если мы перемещаемся >>
в конец while
цикл, это едва значительно потрудилось.
$ time ./junk2.sh
...
./junk2.sh 2.46s user 3.18s system 22% cpu 25.659 total
Но ответ Жабр находит способ только работать date
однажды, и не удивительно, это намного быстрее:
$ time ./bash-gprof junk3.sh
Lines written: 14401
./bash-gprof junk3.sh 0.10s user 0.16s system 96% cpu 0.264 total
$ strace -c ./bash-gprof junk3.sh
Lines written: 14401
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
97.63 0.039538 5648 7 3 waitpid
2.37 0.000961 37 26 writev
0.00 0.000000 0 9 read
...
0.00 0.000000 0 4 clone
$ gprof ./bash-gprof gmon.out
Flat profile:
Each sample counts as 0.01 seconds.
no time accumulated
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
0.00 0.00 0.00 1162 0.00 0.00 xmalloc
0.00 0.00 0.00 782 0.00 0.00 mbschr
0.00 0.00 0.00 373 0.00 0.00 shell_getc
7 waitpids
и 4 clones
по сравнению с 28 806 и 14403 в оригинале!
Таким образом, мораль: Если необходимо назвать внешнюю команду в цикле, который много раз повторяется, Вы или должен найти способ переместить его из цикла или переключиться на язык программирования, который не должен называть внешнюю команду, чтобы сделать работу.
Согласно просьбе, тест на основе метода Iain (измененный для использования тех же имен переменной и цикличного выполнения):
#!/bin/bash
datein=junk.$$.datein
file=junk.$$
((secY2=3600*4))
cnt=0
secBeg=$(date --date="2010-01-01 00:00:00" +%s)
secEnd=$((secBeg+secY2))
((sec=secBeg))
while ((sec<=secEnd)) ; do
echo @$sec >>"$datein"
((sec+=1))
((cnt+=1))
done
date --file="$datein" '+%Y-%m-%d %H:%M:%S' >>"$file"
ls -l "$file"
rm "$datein"
echo Lines written: $cnt
Результаты:
$ time ./bash-gprof ./junk4.sh
Lines written: 14401
./bash-gprof ./junk4.sh 0.92s user 0.20s system 94% cpu 1.182 total
$ strace -c ./junk4.sh
Lines written: 14401
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
91.71 0.116007 14501 8 4 waitpid
3.70 0.004684 0 14402 write
1.54 0.001944 0 28813 close
1.35 0.001707 0 72008 1 fcntl64
0.88 0.001109 0 43253 rt_sigprocmask
0.45 0.000566 0 28803 dup2
0.36 0.000452 0 14410 open
$ gprof ./bash-gprof gmon.out
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
22.06 0.15 0.15 __gconv_transform_utf8_internal
16.18 0.26 0.11 mbrtowc
7.35 0.31 0.05 _int_malloc
5.88 0.35 0.04 __profile_frequency
4.41 0.38 0.03 345659 0.00 0.00 readtok
4.41 0.41 0.03 _int_free
2.94 0.43 0.02 230661 0.00 0.00 hash_search
2.94 0.45 0.02 28809 0.00 0.00 stupidly_hack_special_variables
1.47 0.46 0.01 187241 0.00 0.00 cprintf
1.47 0.47 0.01 115232 0.00 0.00 do_redirections
Так close
и open
обнаруживаются.
Теперь наблюдение Eelvex о >>
на строку по сравнению с >
вокруг while
цикл начинает иметь значение.
Давайте факторизуем его...
#!/bin/bash
datein=junk.$$.datein
file=junk.$$
((secY2=3600*4))
cnt=0
secBeg=$(date --date="2010-01-01 00:00:00" +%s)
secEnd=$((secBeg+secY2))
for ((sec=secBeg; sec<=secEnd; sec=sec+1)) ; do
echo @$sec
((cnt+=1))
done >"$datein"
date --file="$datein" '+%Y-%m-%d %H:%M:%S' >>"$file"
ls -l "$file"
rm "$datein"
echo Lines written: $cnt
$ time ./junk6.sh
Lines written: 14401
./junk6.sh 0.58s user 0.14s system 95% cpu 0.747 total
$ strace -c junk6.sh
Lines written: 14401
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
97.41 0.092263 11533 8 4 waitpid
2.06 0.001949 0 43252 rt_sigprocmask
0.53 0.000506 0 14402 write
0.00 0.000000 0 13 read
0.00 0.000000 0 10 open
0.00 0.000000 0 13 close
0.00 0.000000 0 1 execve
$ gprof ./bash-gprof gmon.out
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
10.00 0.05 0.05 72025 0.00 0.00 expand_word_internal
10.00 0.10 0.05 __gconv_transform_utf8_internal
8.00 0.14 0.04 __profile_frequency
8.00 0.18 0.04 _int_malloc
4.00 0.20 0.02 1355024 0.00 0.00 xmalloc
4.00 0.22 0.02 303217 0.00 0.00 mbschr
Который является также очень, намного быстрее, чем исходный сценарий, но немного медленнее что Gilles.
Можно просто использовать Debian column
. Это предоставляет возможность -n
который заставляет его работать точно, как Вы хотите.
С другой стороны, можно поместить пространство в пустые столбцы, с помощью sed
:
sed ':x s/\(^\|\t\)\t/\1 \t/; t x' < in.tsv | column -t -s $'\t'
пример:
$ sed ':x s/\(^\|\t\)\t/\1 \t/; t x' < in.tsv | column -t -s $'\t'
A B C D
b1 d1
d2
a3 d3
sed 's/||/| |/g;s/||/| |/g' filename-here
Вышеприведенная команда предназначена для трубы, поэтому замените ее на табличку.
Нужно просто заменить пустые столбцы на пустое место и передать вывод на уже используемую команду.
\1
с пустой строкой? относительно – Grzegorz Wierzowiecki 13.01.2012, 16:22\(^\)
один соответствия пустая строка, привязанная на начале строки.\1
"производит копию" той пустой строки. форма жизни – angus 13.01.2012, 18:22