Вопрос или проблема
Как получить текущее время Unix в миллисекундах (т.е. количество миллисекунд с эпохи Unix 1 января 1970 года)?
Это:
date +%s
вернет количество секунд с момента эпохи.
Это:
date +%s%N
возвращает количество секунд и текущие наносекунды.
Таким образом:
date +%s%N | cut -b1-13
даст вам количество миллисекунд с момента эпохи – текущие секунды плюс первые три цифры наносекунд.
и от MikeyB – echo $(($(date +%s%N)/1000000))
(деление на 1000 приведет только к микросекундам)
Вы можете просто использовать %3N
, чтобы усечь наносекунды до трех наиболее значащих цифр (которые затем становятся миллисекундами):
$ date +%s%3N
1397392146866
Это работает, например, на моем Kubuntu 12.04 (Precise Pangolin).
Но учтите, что %N
может не быть реализовано в зависимости от вашей целевой системы. Например, на встроенной системе (корневая файловая система buildroot, собранная с использованием без-FP ARM кросс-компилятора) не было %N
:
$ date +%s%3N
1397392146%3N
(А также на моем (нерутированном) Android планшете нет %N
.)
date +%N
не работает на OS X, но вы можете использовать одно из:
- Ruby:
ruby -e 'puts Time.now.to_f'
- Python:
python -c 'import time; print(int(time.time() * 1000))'
- Node.js:
node -e 'console.log(Date.now())'
- PHP:
php -r 'echo microtime(TRUE);'
- Elixir:
DateTime.utc_now() |> DateTime.to_unix(:millisecond)
- Perl:
perl -e 'use Time::HiRes qw(time); printf "%.3f\n", time;'
- Интернет:
wget -qO- http://www.timeapi.org/utc/now?\\s.\\N
- или для миллисекунд, округленных до ближайшей секунды
date +%s000
Мое решение не является самым лучшим, но оно сработало для меня:
date +%s000
Мне нужно было просто преобразовать дату, например, 2012-05-05, в миллисекунды.
Просто раскрывая это, считаю, что правильная формула с делением будет:
echo $(($(date +%s%N)/1000000))
Для людей, предлагающих запускать внешние программы для получения миллисекунд… при такой скорости, вы также можете сделать следующее:
wget -qO- http://www.timeapi.org/utc/now?\\s.\\N
Суть в том, что перед выбором любого ответа отсюда, пожалуйста, имейте в виду, что не все программы будут работать за одну целую секунду. Измеряйте!
Если вы ищете способ отобразить длительность выполнения вашего скрипта, приведенное ниже даст (не совсем точный) результат:
Как можно ближе к началу вашего скрипта введите следующее
basetime=$(date +%s%N)
Это даст вам начальное значение, например, 1361802943996000000.
В конце вашего скрипта используйте следующее
echo "runtime: $(echo "scale=3;($(date +%s%N) - ${basetime})/(1*10^09)" | bc) seconds"
что отобразит что-то вроде
runtime: 12.383 seconds
Примечания:
(1*10^09) может быть заменено на 1000000000, если хотите
"scale=3"
— это довольно редкая настройка, которая заставляет bc
делать то, что вам нужно. Существует множество других!
Я проверил это только на Windows 7/MinGW… у меня нет нормального *nix-бокса под рукой.
Еще одно решение для MacOS: GNU Coreutils
Я заметил, что версия date
в MacOS не интерпретирует последовательность формата %N
как наносекунды, а просто выводит N
, когда я начал использовать мой скрипт .bashrc
из Linux, который измеряет, как долго выполняются команды, на машине с MacOS.
После небольшого исследования я узнал, что только GNU date из пакета GNU Coreutils поддерживает миллисекунды. К счастью, его довольно легко установить на MacOS, используя Homebrew:
brew install coreutils
Поскольку в пакете содержатся исполняемые файлы, которые уже присутствуют в MacOS, исполняемые файлы Coreutils будут установлены с префиксом g
, поэтому date
будет доступен как gdate
.
Подробности можно найти, например, на этой странице.
Дополнительное решение, так как этот вопрос был задан в 2014 году. Bash 5.0 (релиз 2019 года) ввел две новые встроенные переменные
EPOCHREALTIME – Количество секунд с момента эпохи Unix в виде числа с плавающей точкой с точностью до микросекунд
EPOCHSECONDS – Количество секунд с момента эпохи Unix
Чтобы получить миллисекунды с момента эпохи, вы можете усечь последние три цифры микросекунд, чтобы получить миллисекунды с помощью ${EPOCHREALTIME::-3}
и избежать (дорогостоящего) вызова date
или других внешних программ.
Для Alpine Linux (многие образы Docker) и, возможно, других минимальных Linux окружений, вы можете использовать adjtimex
:
adjtimex | awk '/(time.tv_usec):/ { printf("%06d\n", $2) }' | head -c3
adjtimex
используется для чтения (и задания) переменных времени ядра. С помощью awk
вы можете получить микросекунды, а с помощью head
использовать только первые 3 цифры.
Я не знаю, насколько этот командный метод надежен.
Примечание: Бессовестно украдено из этого ответа
Вот как получить время в миллисекундах без выполнения деления. Возможно, это быстрее…
# test=`date +%s%N`
# testnum=${#test}
# echo ${test:0:$testnum-6}
1297327781715
Обновление: еще одна альтернатива на чистом Bash, которая работает только с Bash 4.2+ – та же, что и выше, но используйте printf
для получения даты. Это определенно будет быстрее, потому что ни один процесс не будет разветвляться от основного.
printf -v test '%(%s%N)T' -1
testnum=${#test}
echo ${test:0:$testnum-6}
Еще одна особенность этого метода заключается в том, что ваша реализация strftime
должна поддерживать %s
и %N
, что не является случаем на моей тестовой машине. См. man strftime
для поддерживаемых параметров. Также см. man bash
для получения синтаксиса printf
. -1
и -2
– это специальные значения для времени.
(повтор из предыдущих ответов) date +%N
не работает на OS X, но вы также можете использовать:
Perl (требуется модуль Time::Format). Возможно, это не лучший модуль CPAN, но он выполняет работу. Time::Format обычно доступен в дистрибутивах.
perl -w -e'use Time::Format; printf STDOUT ("%s.%s\n", time, $time{"mmm"})'
Наиболее точная отметка времени, которую мы можем получить для Mac OS X, вероятно, это:
python3 -c 'import datetime; print(datetime.datetime.now().strftime("%s.%f"))'
1490665305.021699
Но мы должны помнить, что это займет около 30 миллисекунд на выполнение.
Мы можем уменьшить его до масштаба двухзначной дроби и сразу вычислить среднюю затрата на чтение времени, а затем вычесть ее из измерения. Вот пример:
function getTimestamp {
echo `python -c 'import datetime; print datetime.datetime.now().strftime("%s.%f")' | cut -b1-13`
}
function getDiff {
echo "$2-$1-$MeasuringCost" | bc
}
prev_a=`getTimestamp`
acc=0
ITERATIONS=30
for i in `seq 1 $ITERATIONS`;do
#echo -n $i
a=`getTimestamp`
#echo -n " $a"
b=`echo "$a-$prev_a" | bc`
prev_a=$a
#echo " diff=$b"
acc=`echo "$acc+$b" | bc`
done
MeasuringCost=`echo "scale=2; $acc/$ITERATIONS" | bc`
echo "average: $MeasuringCost sec"
t1=`getTimestamp`
sleep 2
t2=`getTimestamp`
echo "measured seconds: `getDiff $t1 $t2`"
Вы можете раскомментировать команды echo, чтобы лучше понять, как это работает.
Результаты этого скрипта, как правило, одни из следующих 3 результатов:
measured seconds: 1.99
measured seconds: 2.00
measured seconds: 2.01
https://github.com/ysoftwareab/nanoseconds
Я только что создал этот кросс-платформенный проект с помощью golang, чтобы отобразить наносекунды с эпохи Unix. Все так просто. Скачивайте исполняемый файл для вашей платформы из релиза Github.
Получение детализированных отметок времени важно для бенчмарков (например, OpenTelemetry). В зависимости от GNU/coreutils/вставьте-язык-программирования во многих случаях не подходящий вариант.
Использование date и expr может помочь вам достичь этого
т.е.
X=$(expr \`date +%H\` \\* 3600 + \`date +%M\` \\* 60 + \`date +%S\`)
echo $X
Просто расширьте это, чтобы сделать все, что вы хотите
Я понимаю, что это не дает миллисекунд с эпохи, но это может быть полезным ответом для некоторых случаев. Это все зависит от того, для чего вам это нужно, просто умножьте на 1000, если вам нужно число в миллисекундах 😀
Самый простой способ – создать небольшой исполняемый файл (например, на C) и сделать его доступным для скрипта.
Не добавляя ничего революционного поверх принятого ответа, но просто сделать это удобно повторно использовать для тех из вас, кто новичок в Bash. Заметьте, что этот пример работает в OS X и на старых версиях Bash, что было требованием для меня лично.
nowInMs() {
echo "$(($(date +'%s * 1000 + %-N / 1000000')))"
}
Теперь вы можете выполнять
TIMESTAMP="$(nowInMs)";
Соединяя предыдущие ответы воедино, когда вы находитесь в OS X,
ProductName: Mac OS X
ProductVersion: 10.11.6
BuildVersion: 15G31+
вы можете сделать следующее
microtime() {
python -c 'import time; print time.time()'
}
compute() {
local START="$(microtime)"
#$1 это команда, $2 ее аргументы
local END="$(microtime)"
DIFF="$(bc <<< "$END - $START")"
echo "$1\t$2\t$DIFF"
}
Решение на Perl
perl -mTime::HiRes -e 'printf "%.0f\n", (Time::HiRes::time() * 1000 )'
Time::HiRes::time() возвращает число с плавающей точкой в виде unixtimeinsec.microseconds
Умножьте на 1000, чтобы сдвинуть влево на 3 цифры, и выводите без десятичных показателей.
Почему бы просто не преобразовать в целое число с %d?
Потому что оно может переполнить знаковое (или беззнаковое) целое на 32-битной ОС, таких как наши древние серверы AIX.
Как уже упоминалось, это вопрос портабельности. Принятый ответ работает на Linux или на любом, что может запускать gnu date, но не на нескольких других UNIX-системах. Лично я обнаруживаю, что на наших старых системах perl гораздо скорее окажется установленным, чем python, Node.js, Ruby или PHP.
Да, date +%s%3N
(если доступно) примерно в 5 раз быстрее, чем perl).
Если вам нужно простое вычисление времени в Shell, это легко и портативно, используя ответ Фрэнка Тонига:
now() {
python -c 'import datetime; print datetime.datetime.now().strftime("%s.%f")'
}
seismo:~$ x=`now`
seismo:~$ y=`now`
seismo:~$ bc <<<$ "$y - $x"
5.212514
cat /proc/uptime
вернет значение в xx.yy [секундах], так что просто умножьте его на 1000, чтобы получить в миллисекундах
Ответ или решение
Получение текущего времени Unix в миллисекундах с помощью Bash — это задача, которая может оказаться не такой простой, как кажется, из-за ограничений на разных системах. Unix-время отсчитывается с эпохи, которая начинается с 00:00:00 UTC 1 января 1970 года, и обычно представляется в секундах. Однако в некоторых случаях требуется более высокая точность, например, в миллисекундах. Это может использоваться для ведения точных логов или временной синхронизации различных событий в системах.
Теория:
Чтобы получить текущее Unix-время в миллисекундах, сначала нужно понять различия между различными единицами времени, которые можно извлечь с помощью команды date
. По умолчанию date +%s
возвращает количество секунд, прошедших с эпохи Unix. Команда date +%s%N
добавляет количество наносекунд, предложенных на текущий момент, но данная функциональность не поддерживается во всех версиях date
. Поэтому другие подходы, такие как выполнение вычислений внутри shell-скрипта или использование иных утилит, становятся актуальными.
Пример:
Одним из решений может быть использование выражения echo $(($(date +%s%N)/1000000))
. Здесь мы получаем секунды и наносекунды с помощью date +%s%N
, после чего делим результат на 1 000 000 для получения миллисекунд. Эта команда преобразует наносекунды в миллисекунды, которые являются средней между секундами и наносекундами.
Тем не менее, как отмечалось в вопросе, формат %N
может не поддерживаться во всех системах. Например, на MacOS необходимо установить GNU Coreutils для поддержки этого формата. После установки GNU Coreutils команда date
заменяется на gdate
, что позволяет использовать %N
. Альтернативно, для получения Unix времени можно использовать программы на Python, Perl, Ruby или Node.js, так как они предоставляют кроссплатформенное решение, не зависящее от версии date
.
Применение:
Допустим, вы разработчик, который пишет shell-скрипты для логирования времени выполнения задачи. Это может потребоваться для точного измерения бизнес-процессов или оценки производительности. На вашей системе поддержка %N
может отсутствовать. В этом случае использование Python может быть спасительным:
python -c 'import time; print(int(time.time() * 1000))'
Эта команда запускает Python-интерпретатор, который импортирует модуль времени и выводит текущее Unix-время, домноженное на 1000 для получения миллисекунд.
Другой вариант — использовать встроенные переменные в Bash 5.0+: EPOCHREALTIME
и EPOCHSECONDS
. Доказано, что они обеспечивают точность без необходимости обращения к внешним программам, что может повысить производительность, так как это избегает forking процессов и дополнительной обработки.
echo ${EPOCHREALTIME::-3}
На примере использования встроенных средств Bash можно демонстрировать, как современные версии операционных систем развиваются, предлагая более простые и эффективные решения для знакомых задач. Также важно учитывать аспекты переносимости и зависимости от системных утилит, выбирая подход для получения данных о времени. Это особенно важно в контексте создания скриптов, которые должны быть совместимы с различными средами разработок и операционными системами.
Таким образом, понимание различных подходов к получению текущего Unix времени в миллисекундах в Bash позволяет выбрать наиболее подходящий инструмент с учётом системных ограничений и требований проекта, предлагая гибкость в настройке скриптов и повышая точность измерений.