ksh_timer 时间接口

在每个函数或用户定义部分的 shell 脚本中用于评估函数执行时间的接口

ksh_timer 帮助 ksh 脚本开发人员或管理员使用最少的更改快速找出其脚本中哪些函数或用户定义部分比较耗时。默认情况下,ksh_timer 为整个脚本以及脚本中定义和使用的每个函数计时。用户可以定义需要计时的其他脚本部分。

Alexandre Blancke, AIX 开发支持专家, IBM

http://www.ibm.com/developerworks/aix/library/au-aix-ksh-timer/alexander.jpgAlexandre Blancke 是 AIX 领域的一名 IBM AIX 开发支持专家。他目前负责提供 AIX 命令(包括 ksh)、库和安全性方面支持,他在管理和支持 AIX 操作系统方面有着 22 年的丰富经验,是一名拥有 IBM 认证的产品服务专业人士。



2013 年 1 月 21 日

简介

ksh_timer 被设计用来帮助 ksh 或 ksh93 脚本管理员或开发人员计算每个函数及其 shell 脚本用户定义部分所用的时间。

以下几个要点定义了 ksh_timer 的功能:

  • 如果在 ksh_timer 中运行脚本,那么该脚本调用的函数是自动计时的。
  • 用户使用 ##KTB <section_name>(这部分的开始)和 ##KTE <section_name>(这部分的结束)在脚本中心定义相关部分。
  • 给出的时间是总运行时间,不是已使用的时间。
  • 输出包括 « Main » 时间,这是脚本的总运行时间。
  • 还包括去向不明的输出时间,这些时间是花费在函数或用户定义部分之外的 « Main » 部分中的时间。
  • 去向不明的时间还包括计算函数引起的 ksh_timer 开销。
  • ksh_timer 支持 ksh 和 ksh93 脚本的执行。
  • 使用:ksh_timer [-D] [-c] [-?] [-s] [script name arguments ...]
    • -D 将 ksh_timer 置于调试模式。
    • -c 保留原始输出文件 (ksh_timer.<script_name>.<pid>.out)。通常在执行后删除。
    • -s [script name arguments ...] 脚本文件名和脚本所需参数。
    • -? 打印使用情况。

重要提示

该接口 (ksh_timer) 是为了用户方便而提供的。本文使用一个样例程序说明了 ksh_timer 的用法和功能,工具是按原样提供的,我们不提供任何担保和支持。

该工具已在 IBM® AIX® 5.3,6.1 和 7.1 版本上进行了测试。ksh_timer 可以用于其他操作系统,要实现这一点,必须使用本文安装 小节中提供的源代码,为该操作系统重新构建 ksh_timer_c 可执行文件。

概述

在启动时,ksh_timer 会读取命令行提供的输入脚本,然后从中构建以下 3 个文件(源文件、计时器文件和 exec 文件)。

  • 源文件包含原函数定义,并且来自 exec 文件。
  • 计时器文件包含包括计时代码和调用原函数的封装函数。计时器文件也来自 exec 文件。在下列示例中,原函数 f1 在源文件中被重命名为 f1_orig ,而在计时器文件中,f1 有一个计时代码包含 f1_orig 函数,该函数使我们能够得到原函数 f1 所耗的时间。
  • exec 文件是源文件和计时器文件的来源,而且包含原始 main 代码以及用于任何用户定义部分的计时器代码。

当完成这些文件的构建之后,ksh_timer 会运行构建 exec 文件。运行原始代码时,该函数会计算函数和用户定义部分的运行时间,并将这些时间写入输出文件。执行完这些操作之后,它会分析输出文件,然后计算函数名、缩进、迭代次数以及这些函数或代码部分的最小、最大、平均和总运行时间,并将它们显示在屏幕上。

  • 使用以下输入脚本样例:ksh_timer.test_pres
#! /usr/bin/ksh
f1()
{
echo "hello"
}
#Main
##KTB user_defined_section
sleep 1
f1##KTE user_defined_section
sleep 2
f1
  • 源文件:包含原始函数定义
f1_orig()
{
echo "hello"
}
  • 计时器文件:包含包括计时代码和调用原函数的封装函数
function f1
{
time1f1=`/home/albl/ksh_timer/ksh_timer_c`
ksh_timer_add_func f1
f1_orig $*
f1_orig_rc=$?
time2f1=`/home/albl/ksh_timer/ksh_timer_c`
let " time3f1 = $time2f1 - $time1f1"
print "$indent1:$indent2:$indent3:$indent4:$indent5:$time3f1" >&9
ksh_timer_remove_func f1
return $f1_orig_rc
}
  • exec 文件:包含用于任何用户定义部分的原始代码和计时器代码
. ./ksh_timer.test_pres.413914.source
. ./ksh_timer.test_pres.413914.timer
time1user_defined_section=`/home/albl/ksh_timer/ksh_timer_c`
ksh_timer_add_func user_defined_section
sleep 1
f1
time2user_defined_section=`/home/albl/ksh_timer/ksh_timer_c`
let "time3user_defined_section=$time2user_defined_section-$time1user_defined_section"
print "$indent1:$indent2:$indent3:$indent4:$indent5:$time3user_defined_section" >&9
ksh_timer_remove_func user_defined_section
sleep 2
f1
  • 运行该样例得到下列输出:
$ ./ksh_timer -s ./sample
----------------------Start of sample output--------------------------------
hello
hello
----------------------End of sample output--------------------------------
------------------------------------------------------------------------------------------
|    function   |     Minimum       |     Maximum       |   Average  |       Total       |
|               +-------------------+-------------------+------------+-------------------|
|   or Section  |iter.#|   time     |iter.#|   time     |   time     |#iter.|   time     |
|---------------+------+------------+------+------------+------------+------+------------|
|Main           |                                                           |0m03.092834s|
|---------------+------+------------+------+------------+------------+------+------------|
|-user_defined|     1|0m01.027853s|     1|0m01.027853s|0m01.027853s|     1|0m01.027853s|
|--f1           |     1|0m00.003143s|     1|0m00.003143s|0m00.003143s|     1|0m00.003143s|
|-f1            |     1|0m00.002652s|     1|0m00.002652s|0m00.002652s|     1|0m00.002652s|
|---------------------------------------------------------------------------+------------|
|-Unaccounted   |                                                           |0m02.062329s|
------------------------------------------------------------------------------------------

正如我们所看到的,user_defined_section 被调用了一次,在 user_defined_section 中 f1 函数被调用了一次,在该脚本的主要部分中,f1 也被调用了一次。user_defined_section 所用时间是 1 秒多一点,这主要是步骤 1 以及 f1 函数所用的时间。去向不明时间大约有 2 秒,这主要是所有部分或函数外部的步骤 2 所用时间,以及 ksh_timer 计算时间所需的开销。

用户定义部分计时器代码与执行代码保持关联,以便能够在这些部分的内部使用传递给 shell 脚本的参数。ksh_timer 输出将以一种易于阅读的表格形式发送到屏幕(稍后给出相关示例)。如果您选择将输出发送到某个文件,那么将以下列格式输出每次迭代:

Main:indent1:…:indent9:>time_spent<

(缩进 1 到 9 的输出可能是调用函数的函数名,或者如果函数深度没有达到那么深的话,输出则为空。)例如:

Main:user_defined_section:f1:::::::3143

(我们在 f1 函数中花费的时间为 3143 微秒,user_defined 调用了该函数,而 user_defined 反过来是从主要部分调用的。)

安装 ksh_timer

  • 您可以从 下载 部分下载 ksh_timer
  • 要完成安装,首先应该将 ksh_timer.tar 文件解压到任意目录下。
  • 您可能需要编辑将要实现计时功能的原始脚本来添加带有 « ##KTB <section_name> » 和 « ##KTE <section_name> 定义的用户定义部分。
  • 要完成执行,请运行 ./ksh_timer –s <your_script>。
  • 输出被发送到屏幕上;如果指定了 –c 的话,则会将原始数据发送到文件 ksh_timer.<script name>.<PID>.out。
  • 如果运行一个非 AIX 系统,ksh_timer_c 代码需要从以下 ksh_timer_c.c 源代码重构:
#include <stdio.h>
#include <sys/time.h>
main()
{
struct timeval Tp;
gettimeofday(&Tp,NULL);
printf("%lld\n",(unsigned long long)((Tp.tv_sec*1000000)+Tp.tv_usec));
}

使用示例

想象一下,如果有下列脚本 script_test_vmm,您可能想要评估 vmm_test2 所用时间,该函数在每次调用 rmss 之后才调用。您可能想要评估每个 rmss 所用的时间。

#! /usr/bin/ksh
integer i=0
rmss -c 950
while [[ $i -lt 3 ]]
  do
  /tmp/ksh_timer/test_vmm2
  i=i+1
  done

rmss -c 1450
i=0
while [[ $i -lt 3 ]]
  do
  /tmp/ksh_timer/test_vmm2
  i=i+1
  done
  • 步骤 1:通过使用适当的 ##KTB 并匹配的 ##KTE,编辑您的 script_test_vmm 脚本和添加用户定义部分,从而划分您的用户定义部分的界限,如下列代码所示:
#! /usr/bin/ksh
integer i=0
##KTB rmss950
rmss -c 950
while [[ $i -lt 3 ]]
  do
##KTB vmm_test2
  /tmp/ksh_timer/test_vmm2
  i=i+1
##KTE vmm_test2
  done
##KTE rmss950
##KTB rmss1450
rmss -c 1450
i=0
while [[ $i -lt 3 ]]
  do
##KTB vmm_test2
  /tmp/ksh_timer/test_vmm2
  i=i+1
##KTE vmm_test2
  done
##KTE rmss1450
  • 步骤 2:使用 ksh_timer -s script_test_vmm 运行先前的脚本。
----------------------Start of script_test_vmm output--------------------------------
Simulated memory size changed to 950 Mb.
Simulated memory size changed to 1450 Mb.
----------------------End of script_test_vmm output--------------------------------
------------------------------------------------------------------------------------------
|    Function   |     Minimum       |     Maximum       | Average    |     Total         |
|               +-------------------+-------------------+------------+-------------------|
|   or Section  |iter.#|   time     |iter.#|   time     |  time      |#iter.|  time      |
|---------------+------+------------+------+------------+------------+------+------------|
|Main           |                                                           |1m36.578814s|
|---------------+------+------------+------+------------+------------+------+------------|
|-rmss950      |     1|1m02.120269s|     1|1m02.120269s|1m02.120269s|     1|1m02.120269s|
|--vmm_test2   |     3|0m16.813174s|     1|0m23.461610s|0m20.654322s|     3|1m01.962968s|
|-rmss1450     |     1|0m34.416279s|     1|0m34.416279s|0m34.416279s|     1|0m34.416279s|
|--vmm_test2   |     2|0m11.382110s|     3|0m11.427596s|0m11.402585s|     3|0m34.207757s|
|---------------+-----------------------------------------------------------+------------|
|-Unaccounted   |                                                           |0m00.042266s|
------------------------------------------------------------------------------------------

作为另一个示例,我们在 mksysb 脚本上运行 ksh_timer。通过编辑 mksysb 脚本,可以添加一个名为 backup_files_in_list 的用户定义部分。在以下输出中我们可以看到,不仅仅对 backup_files_in_list 部分进行计时,而且对在脚本中定义的任何部分都进行了计时。通过查看输出,我们很容易发现,mksysb 脚本所用的最大时间在 backup_files_in_list 中,耗时 1m33.029798s,整个脚本耗时 1m49.140307s

# ksh_timer -s ./mksysb -i /dev/null
----------------------Start of mksysb output--------------------------------
Creating information file (/image.data) for rootvg.
Creating list of files to back up
Backing up 56510 files........
56510 of 56510 files backed up (100%)0512-038 mksysb: Backup Completed Successfully.
----------------------End of mksysb output--------------------------------
------------------------------------------------------------------------------------------
|    Function   |    Minimum        |    Maximum        |    Average |     Total         |
|               +-------------------+-------------------+------------+-------------------|
|   or Section  |iter.#|  time      |iter.#|  time      |  time      |#iter.|  time      |
|---------------+------+------------+------+------------+------------+------+------------|
|Main           |                                                           |1m49.140307s|
|---------------+------+------------+------+------------+------------+------+------------|
|-set_up_environ|     1|0m00.048720s|     1|0m00.048720s|0m00.048720s|     1|0m00.048720s|
|--fail_if_wpar |     1|0m00.005507s|     1|0m00.005507s|0m00.005507s|     1|0m00.005507s|
|--getoptions   |     1|0m00.012926s|     1|0m00.012926s|0m00.012926s|     1|0m00.012926s|
|-create_temp_di|     1|0m00.007239s|     1|0m00.007239s|0m00.007239s|     1|0m00.007239s|
|-if_rootvg     |     1|0m00.153775s|     1|0m00.153775s|0m00.153775s|     1|0m00.153775s|
|--tddupdate    |     1|0m00.070964s|     1|0m00.070964s|0m00.070964s|     1|0m00.070964s|
|--Get_Rootvg_IS|     1|0m00.042464s|     1|0m00.042464s|0m00.042464s|     1|0m00.042464s|
|--tsddupdate   |     1|0m00.027835s|     1|0m00.027835s|0m00.027835s|     1|0m00.027835s|
|-if_device     |     1|0m00.014855s|     1|0m00.014855s|0m00.014855s|     1|0m00.014855s|
|-if_image_data |     1|0m05.132797s|     1|0m05.132797s|0m05.132797s|     1|0m05.132797s|
|-make_backup_da|     1|0m00.350696s|     1|0m00.350696s|0m00.350696s|     1|0m00.350696s|
|--get_backup_si|     1|0m00.031814s|     1|0m00.031814s|0m00.031814s|     1|0m00.031814s|
|---get_stanza_d|     1|0m00.019862s|     1|0m00.019862s|0m00.019862s|     1|0m00.019862s|
|-run_the_exclud|     1|0m05.697004s|     1|0m05.697004s|0m05.697004s|     1|0m05.697004s|
|-backup_files|     1|1m33.029798s|     1|1m33.029798s|1m33.029798s|     1|1m33.029798s|
|--kill_pid     |     1|0m02.015270s|     2|0m02.020543s|0m02.017906s|     2|0m04.035813s|
|-done_backup   |     1|0m00.016388s|     1|0m00.016388s|0m00.016388s|     1|0m00.016388s|
|--kill_pid     |     1|0m02.015329s|     1|0m02.015329s|0m02.015329s|     1|0m02.015329s|
|---------------------------------------------------------------------------+------------|
|-Unaccounted   |                                                           |0m04.689035s|
------------------------------------------------------------------------------------------
  • 使用第 2 个示例,您也可以轻松地发现是否重复调用了一个函数或某个部分,或者是否有些调用比其他调用更加耗时。例如,在这里您可以看出,setcmdinit_pos_cmds 中调用了 44 次,我们还可以看出,最大时间、最小时间和平均时间基本上都是常量。

第 3 个示例使用了 ksh_timer –s ./rpm_share –i 结果,该示例展示了 ksh_timer 中缩进的使用,以及可在输出中找到的各种信息。例如,我们可以发现:

  • 在其他两个函数中也调用了 setcmd:在 init_pos_cmd 中调用了 44 次,在 init_sys_vars 中调用了 5 次。
  • 查看最小时间、最大时间和平均时间,我们可以看到,不同 setcmd 迭代之间没有太大差量,不管是在 init_pos_cmd 调用还是从 init_sys_vars 调用。
# ksh_timer –s ./rpm_share –i …
-----------------Start of rpm_share output--------------------------------
-----------------End of rpm_share output--------------------------------
------------------------------------------------------------------------------------------
|    Function   |    Minimum        |     Maximum       |  Average   |       Total       |
|               +-------------------+-------------------+------------+-------------------|
|   or Section  |iter.#|  time      |iter.#|  time      |  time      |#iter.|  time      |
|---------------+------+------------+------+------------+------------+------+------------|
|Main           |                                                           |0m01.506875s|
|---------------+------+------------+------+------------+------------+------+------------|
|-init_baselib  |     1|0m00.366488s|     1|0m00.366488s|0m00.366488s|     1|0m00.366488s|
|--init_pos_cmd |     1|0m00.252172s|     1|0m00.252172s|0m00.252172s|     1|0m00.252172s|
|---setcmd    |    9|0m00.002762s|  5|0m00.002922s|0m00.002825s|44|0m00.124313s|
|--init_sys_vars|     1|0m00.056660s|     1|0m00.056660s|0m00.056660s|     1|0m00.056660s|
|---setcmd    |    5|0m00.002860s|  2|0m00.002929s|0m00.002879s| 5|0m00.014397s|
|---init_libinst|     1|0m00.014667s|     1|0m00.014667s|0m00.014667s|     1|0m00.014667s|
|----inc        |     1|0m00.008601s|     1|0m00.008601s|0m00.008601s|     1|0m00.008601s|
|-----isnum     |     1|0m00.002853s|     1|0m00.002853s|0m00.002853s|     1|0m00.002853s|
|--ck_exp_fs    |     1|0m00.035973s|     1|0m00.035973s|0m00.035973s|     1|0m00.035973s|
|---isYorN      |     1|0m00.002837s|     1|0m00.002837s|0m00.002837s|     1|0m00.002837s|
|---whichfs     |     1|0m00.004191s|     1|0m00.004191s|0m00.004191s|     1|0m00.004191s|
|---getfsfree   |     1|0m00.009434s|     1|0m00.009434s|0m00.009434s|     1|0m00.009434s|
|----isnum      |     1|0m00.002462s|     1|0m00.002462s|0m00.002462s|     1|0m00.002462s|
|---isnum       |     1|0m00.002952s|     1|0m00.002952s|0m00.002952s|     1|0m00.002952s|
|--check_dir    |     1|0m00.002905s|     1|0m00.002905s|0m00.002905s|     1|0m00.002905s|
|-ck_exp_fs     |     1|0m00.036059s|     2|0m00.036169s|0m00.036114s|     2|0m00.072228s|
|--isYorN       |     2|0m00.002875s|     1|0m00.002881s|0m00.002878s|     2|0m00.005756s|
|--whichfs      |     2|0m00.004239s|     1|0m00.004263s|0m00.004251s|     2|0m00.008502s|
|--getfsfree    |     1|0m00.009377s|     2|0m00.009382s|0m00.009379s|     2|0m00.018759s|
|---isnum       |     2|0m00.002425s|     1|0m00.002444s|0m00.002434s|     2|0m00.004869s|
|--isnum        |     2|0m00.003008s|     1|0m00.003030s|0m00.003019s|     2|0m00.006038s|
|-set_time_token|     1|0m00.009403s|     1|0m00.009403s|0m00.009403s|     1|0m00.009403s|
…snip…
|--check_fdup_sp|     1|0m00.098203s|     1|0m00.098203s|0m00.098203s|     1|0m00.098203s|
|---getfsize    |     1|0m00.018850s|     1|0m00.018850s|0m00.018850s|     1|0m00.018850s|
|----check_file |     1|0m00.002347s|     1|0m00.002347s|0m00.002347s|     1|0m00.002347s|
|----isnum      |     1|0m00.002366s|     1|0m00.002366s|0m00.002366s|     1|0m00.002366s|
|---whichfs     |     1|0m00.004378s|     1|0m00.004378s|0m00.004378s|     1|0m00.004378s|
|---addbuf      |     1|0m00.021044s|     1|0m00.021044s|0m00.021044s|     1|0m00.021044s|
|----isnum      |     3|0m00.002948s|     2|0m00.002962s|0m00.002956s|     3|0m00.008868s|
|---ck_exp_fs   |     1|0m00.036967s|     1|0m00.036967s|0m00.036967s|     1|0m00.036967s|
|----isYorN     |     1|0m00.002998s|     1|0m00.002998s|0m00.002998s|     1|0m00.002998s|
|----whichfs    |     1|0m00.004340s|     1|0m00.004340s|0m00.004340s|     1|0m00.004340s|
|----getfsfree  |     1|0m00.009691s|     1|0m00.009691s|0m00.009691s|     1|0m00.009691s|
|-----isnum     |     1|0m00.002514s|     1|0m00.002514s|0m00.002514s|     1|0m00.002514s|
|----isnum      |     1|0m00.002964s|     1|0m00.002964s|0m00.002964s|     1|0m00.002964s|
|--cleanup      |     2|0m00.002960s|     1|0m00.007511s|0m00.005235s|     2|0m00.010471s|
|---------------------------------------------------------------------------+------------|
|-Unaccounted   |                                                           |0m00.141934s|
------------------------------------------------------------------------------------------

故障排除

如果在使用 ksh_timer 工具是发现任何的问题,那么需要运行:

  • ./ksh_timer –D –c –s <your_script> <script.debug.out 2>&1
  • 将下列文件发送给代码作者:
    • script.debug.out
    • <your_script>
    • csv 文件:ksh_timer. <your_script>.<pid>.out

结束语

ksh_timer 是一个简单而又方便的可定制工具,可用于估计脚本所用的时间。不需要修改脚本,只需运行一次,就可以了解该脚本最耗时的函数。如果脚本没有使用函数,或者大部分时间被该脚本的主部分占用,那么用户可以编辑该脚本,并在主要部分、甚至是函数中创建用户定义部分,以便跟踪耗时最多的某一行的详细信息。ksh_timer 输出分析可在耗时最多的用户 shell 脚本中显示部分代码。如果该脚本其中一部分运行了另一个 shell 脚本,那么用户可能需要更改这些代码,以便调用该脚本,使用 ksh_timer 对其进行计时。如果调用的可执行文件不是一个 shell 脚本,那么用户需要使用其他可执行工具对其进行计时。


下载

描述名字大小
ksh_timer 工具ksh_timer.tar30 KB

参考资料

学习

  • 关于 ksh 和 ksh93 的 AIX 文档
  • O'Reilly Learning the Korn Shell,第 2 版
  • 使用 XML-RPC 为 C++ 应用程序启用 Web 服务(developerWorks,2006 年 6 月),一个将 C++ 方法作为服务公开的详细指南。
  • 关注 Twitter 上的 developerWorks
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • AIX and UNIX 下载中心:在这里你可以下载到可以运行在 AIX 或者是 UNIX 系统上的 IBM 服务器软件以及工具,让您可以提前免费试用他们的强大功能。
  • IBM Systems Magazine for AIX 中文版:本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。IBM Systems Magazine 的内容都是由十分资深的业内人士撰写的,包括 IBM 的合作伙伴、IBM 的主机工程师以及高级管理人员。所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用 IBM 系统时有一个更好的认识。

获得产品和技术

  • 以最适合您的方式 评估 IBM 产品:下载产品试用版、在线试用产品、在云环境下试用产品,或者在 SOA Sandbox 中花费几个小时来学习如何高效实现面向服务的体系架构。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX, Open source
ArticleID=855660
ArticleTitle=ksh_timer 时间接口
publish-date=01212013