Універсальна альтернатива, яка не базується на час? [зачинено]


10

Для порівняння виконується раз сценаріїв між різними оболонками, деякі відповіді SE пропонують використовувати bash«и вбудованих time команд, наприклад , так:

time bash -c 'foo.sh'
time dash -c 'foo.sh'

... і т. д. для кожної оболонки для тестування. Такі тести не виключають час , необхідне для кожної оболонки , щоб завантажити і форматувати себе . Наприклад, припустимо, що обидві вищевказані команди зберігалися на повільному пристрої зі швидкістю читання ранньої дискети , (124 КБ / с), dash( виконуваний ~ 150 Кб ) завантажувались приблизно в 7 разів швидше, ніж bash( ~ 1 М ) оболонка Час завантаження перекривило б timeцифри - час попереднього завантаження цих снарядів не має значення для вимірювання часу виконання foo.shпід кожною оболонкою після завантаження снарядів.

Який найкращий портативний і взагалі Util для запуску для синхронізації сценарію , який може бути запущений з всередині кожної оболонки? Отже, наведений вище код буде виглядати приблизно так:

bash -c 'general_timer_util foo.sh'
dash -c 'general_timer_util foo.sh'

Примітка: ніяких вбудованих time команд оболонок немає , оскільки жодна не є портативною або загальною.


Ще краще, якщо утиліта також зможе визначити час, зайнятий внутрішніми командами оболонки та конвеєрами, без того, щоб користувач спершу загортав їх у сценарій. Штучний синтаксис, подібний до цього, допоможе:

general_timer_util "while read x ; do echo x ; done < foo"

Деякі оболонки timeможуть це впоратися. Наприклад bash -c "time while false ; do : ; done"працює. Щоб побачити, що працює (а що ні), у вашій системі спробуйте:

tail +2 /etc/shells | 
while read s ; do 
    echo $s ; $s -c "time while false ; do : ; done" ; echo ----
done

6
Просто використовувати /usr/bin/time?
Kusalananda

1
Я не розумію, як будь -який невбудований може і "усунути час, необхідний для завантаження та ініціалізації кожної оболонки", і виконати окремий сценарій, будучи "портативним та загальним".
Майкл Гомер

1
Це не відповідь на запитання, це вам підкаже уточнити, що ви хочете.
Майкл Гомер

1
Я опублікував свою найкращу спробу, але думаю, що все ще не визначено питання про те, чого саме намагається досягти.
Майкл Гомер

2
Що ви маєте на увазі під "портативним чи загальним"? Оболонки вбудованої оболонки є настільки портативними (робота на багатьох системах) і загальнішими (робота в інших обставинах, оскільки вони можуть зайняти часом щось інше, ніж виконання файлу), як зовнішня команда. Яку проблему ви намагаєтеся вирішити?
Жиль "ТАК - перестань бути злим"

Відповіді:


10

Слід зазначити, що timeвизначено POSIX , а AFAICT - єдиний варіант, який POSIX згадує ( -p) правильно підтримується різними оболонками:

$ bash -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ dash -c 'time -p echo'

real 0.01
user 0.00
sys 0.00
$ busybox sh -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ ksh -c 'time -p echo'       

real 0.00
user 0.00
sys 0.00

1
Проблема полягає в тому, що щоб мати можливість порівнювати терміни, результат повинен бути приурочений тим самим виконанням time. Це порівняно з тим, щоб дозволити спринтерам вимірювати свій власний час на 100 м, окремо, замість того, щоб робити це одним годинником одночасно. Це, очевидно, задирки, але все ж ...
Кусалананда

@Kusalananda Я подумав, що проблема полягає в тому, що ОП вважає, що timeце не портативно; це здається портативним. (Я згоден з вашою точкою порівняння)
muru

@muru, у моїй системі dash -c 'time -p while false ; do : ; done'повертається "час: не можна запустити поки: жодного файлу чи каталогу <cr> Команда не вийшла з помилками ненульового стану 127" .
agc

1
@agc POSIX також говорить: "Термін утиліта використовується, а не команда, щоб виділити той факт, що комбіновані команди оболонки, трубопроводи, спеціальні вбудовані тощо не можуть використовуватися безпосередньо. Однак утиліта включає програми для користувачів і сценарії оболонки, а не лише стандартні утиліти. " (див. розділ
RATIONALE


7

Я використовую команду GNU date , яка підтримує таймер високої роздільної здатності:

START=$(date +%s.%N)
# do something #######################

"$@" &> /dev/null

#######################################
END=$(date +%s.%N)
DIFF=$( echo "scale=3; (${END} - ${START})*1000/1" | bc )
echo "${DIFF}"

І тоді я називаю сценарій так:

/usr/local/bin/timing dig +short unix.stackexchange.com
141.835

Вихідний блок знаходиться в мілісекундах.


1
Якщо припустити, що час (епоха) не змінюється між ними. Не можу придумати випадок на практиці, де це могло б викликати проблеми, але все-таки варто згадати.
phk

1
Вам слід додати, що для цього потрібно GNU dateспеціально.
Kusalananda

@phk, будь ласка, поясніть?
Рабін

1
@Rabin Давайте говорити ваші питання клієнта NTP і поновлення і зміни ваш годинник між тим, де STARTі ENDвстановлено, то це, очевидно , впливає на результат. Не маю уявлення, наскільки точно вам це потрібно і чи це має значення у вашому випадку, але, як я вже сказав, щось пам’ятати. (Весела історія: я знаю, що саме це програмне забезпечення призвело до несподівано негативних результатів - воно використовувалось для прорахунку пропускної здатності - який потім перебив декілька речей.)
phk

1
Крім того, хіба деякі клієнти NTP не сповільнюють та не прискорюють годинник, а не роблять «стрибки» в системний час? Якщо у вас є такий клієнт NTP, і ви вчора ввечері зробили деякі таймінги, вони, ймовірно, перекошені клієнтом NTP, "очікуючи" стрибну секунду. (Або системний годинник просто працює до 61 у такому випадку?)
Jörg W Mittag

6

timeУтиліта, як правило , вбудована в оболонку, як ви помітили, що робить його марним в якості «нейтрального» таймера.

Однак, утиліта зазвичай також доступна як зовнішня утиліта, /usr/bin/timeяка цілком може бути використана для виконання запропонованих вами експериментів із синхронізацією.

$ bash -c '/usr/bin/time foo.sh'

Як це "усуває час, необхідний для завантаження та ініціалізації кожної оболонки"?
Майкл Гомер

1
Якщо foo.shвін виконується і має шебанг, то він завжди працює з тією ж оболонкою, і він рахує час запуску цієї оболонки, тож це не те, чого хоче ОП. Якщо foo.shодного з них не вистачає, то це зовсім не працює.
Кевін

@Kevin Дуже правда. Я брав timeдо уваги лише "без вбудованої оболонки ", здається. Час запуску оболонки, можливо, доведеться вимірювати окремо.
Kusalananda

1
Я не знаю жодної оболонки, яка має timeвбудовану команду. Однак у багатьох оболонках, у тому числі bashє timeключове слово, яке можна використовувати для тимчасових трубопроводів. Щоб вимкнути це ключове слово, щоб timeкоманда (у файловій системі) використовувалася, ви можете його цитувати "time" foo.sh. Дивіться також unix.stackexchange.com/search?q=user%3A22565+time+keyword
Stéphane Chazelas

6

Ось таке рішення, яке:

  1. виключити [s] час, необхідний для завантаження кожної оболонки та ініціалізації

  2. можна запускати зсередини кожної оболонки

  3. Використання

    немає вбудованих timeкоманд оболонок , оскільки жодна не є портативною або загальною

  4. Працює у всіх сумісних з POSIX оболонках.
  5. Працює з усіма сумісними з POSIX та XSI-сумісними системами за допомогою компілятора C , або де ви можете заздалегідь скласти виконаний файл C.
  6. Використовує однакові терміни реалізації на всіх оболонках.

Є дві частини: коротка програма C, яка завершується gettimeofday, яка є застарілою, але все ж більш портативною, ніж clock_gettime, і короткий скрипт оболонки, який використовує цю програму, щоб отримати годинник з мікросекундною точністю, читаючи обидві сторони пошуку сценарію. Програма C - це єдиний портативний і мінімальний накладний спосіб отримати підсекундну точність на часовій позначці.

Ось програма С epoch.c:

#include <sys/time.h>
#include <stdio.h>
int main(int argc, char **argv) {
    struct timeval time;
    gettimeofday(&time, NULL);
    printf("%li.%06i", time.tv_sec, time.tv_usec);
}

І сценарій оболонки timer:

#!/bin/echo Run this in the shell you want to test

START=$(./epoch)
. "$1"
END=$(./epoch)
echo "$END - $START" | bc

Це стандартний командний мову оболонки і bcі повинен працювати як скрипт під будь-який POSIX-сумісної оболонки.

Ви можете використовувати це як:

$ bash timer ./test.sh
.002052
$ dash timer ./test.sh
.000895
$ zsh timer ./test.sh
.000662

Він не вимірює системний або користувальницький час, минув лише немонотонний настінний годинник. Якщо системний годинник зміниться під час виконання сценарію, це дасть неправильні результати. Якщо система знаходиться під навантаженням, результат буде ненадійним. Я не думаю, що щось краще може бути переносним між оболонками.

Змінений сценарій таймера може використовувати evalзамість цього для запуску команд поза сценарієм.


Так співпало, що як раз перед читанням цього (і останній рядок про eval), я був щипаючи сценарій в Рабина відповідь «s включити eval "$@", тому він може запустити оболонку вбудованих команд на льоту.
agc

4

Багаторазово переглянуте рішення, використовуючи /proc/uptimeта dc/ bc/ awkу великих частинах, завдяки введенню agc :

#!/bin/sh

read -r before _ < /proc/uptime

sleep 2s # do something...

read -r after _ < /proc/uptime

duration=$(dc -e "${after} ${before} - n")
# Alternative using bc:
#   duration=$(echo "${after} - ${before}" | bc)
# Alternative using awk:
#   duration=$(echo "${after} ${before}" | awk '{print $1 - $2}')

echo "It took $duration seconds."

Очевидно, передбачається, що /proc/uptimeіснує і має певну форму.


3
Це зробило б його портативним між оболонками, але не портативним між реалізаціями Unix, оскільки деяким просто не вистачає /procфайлової системи. Якщо це турбота чи ні, я не знаю.
Кусалаланда

1
Наголос на цьому Q пропонує швидкість дискети або гірше. У такому випадку навантаження awkможе бути значною. Можливо b=$(cat /proc/uptime)до, потім a=$(cat /proc/uptime)після, потім розберіть $ a і $ b і відніміть.
agc

@agc Хороший внесок, спасибі, я додав відповідні альтернативні рішення відповідно.
phk

1
Не думав про це раніше, але якби вбудовані начебто readкраще cat, це було б чистіше (і трохи швидше): read before dummyvar < /proc/uptime ;sleep 2s;read after dummyvar < /proc/uptime; duration=$(dc -e "${after} ${before} - n");echo "It took $duration seconds."
agc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.