使用bash脚本快速生成测试数据(UUID,大型随机数等)

时间:2017-11-18 17:00:28

标签: bash

我有一个小的bash脚本,其中包含一个case语句,如果第一个参数与case参数匹配,它会回显随机数据。 代码如下:

#!/usr/bin/env bash   '

AC='auto-increment'                                                                                                    
UUID='uuid'                                                                                                            
LAT='lat'                                                                                                              
LONG='long'                                                                                                            
IP='ip'

generate_mock_data() {                                                                                                 
# ARGS: $1 - data type, $2 - loop index                                                                              
case ${1} in                                                                                                         
    ${AC})                                                                                                             
        echo ${2} ;;                                                                                                     
    ${UUID})                                                                                                           
        uuidgen ;;                                                                                                       
    ${LAT})                                                                                                            
        echo $((RANDOM % 180 - 90)).$(shuf -i1000000-9999999 -n1) ;;                                                     
    ${LONG})                                                                                                           
        echo $((RANDOM % 360 - 180)).$(shuf -i1000000-9999999 -n1) ;;                                                    
    ${IP})                                                                                                             
        echo $((RANDOM%256)).$((RANDOM%256)).$((RANDOM%256)).$((RANDOM%256)) ;;                                          
esac                                                                                                                 
}

# Writing data to file                                             
headers=('auto-increment' 'uuid' 'lat' 'long' 'ip')                
for i in {1..2500}; do                                             
  for header in "${headers[@]}"; do                                
    echo -n $(generate_mock_data ${header} ${i}),                  
  done                                                             
echo  # New line                                                 
done >> file.csv

但是,只有2500行的执行时间非常慢:

real 0m8.876s user 0m0.576s sys 0m0.868s
我究竟做错了什么 ?我有什么办法可以加快这个过程吗?或者bash不是这类操作的正确语言? 我也尝试分析整个脚本,但在查看日志后我没有注意到任何明显的瓶颈。

2 个答案:

答案 0 :(得分:4)

看起来你可以用Python快速生成一个UUID,所以如果你只执行一次Python来生成2,500个UUID,并且你不是Python程序员 - 就​​像我一样;-)那么你可以使用{{{ 1}}:

awk

我的iMac需要0.06秒。

  • python -c 'import uuid; print("\n".join([str(uuid.uuid4()).upper() for x in range(2500)]))' | awk '{ lat=-90+180*rand(); lon=-180+360*rand(); ip=int(256*rand()) "." int(256*rand()) "." int(256*rand()) "." int(256*rand()); print NR,$0,lat,lon,ip }' OFS=, “输出字段分隔符”
  • OFS是行号
  • $ 0表示“整个输入行”

您可以单独试用Python,如下所示:

NR

答案 1 :(得分:3)

Shell是正确的工具吗?

不是真的,但如果你避免不良做法,你可以快速做出相对的事情。

使用ksh93,下面可靠地以0.5-0.6秒的挂钟运行;用bash,1.2-1.3s。

它看起来像什么?

#!/usr/bin/env bash
# Comment these two lines if running with ksh93, obviously. :)
[ -z "$BASH_VERSION" ]         && { echo "This requires bash 4.1 or newer" >&2; exit 1; }
[[ $BASH_VERSION = [123].* ]]  && { echo "This requires bash 4.1 or newer" >&2; exit 1; }

uuid_stream() {
  python -c '
import uuid
try:
  while True:
    print str(uuid.uuid4()).upper()
except IOError:
    pass # probably an EPIPE because we were closed.
'
}

# generate a file descriptor that emits a shuffled stream of integers
exec {large_int_fd}< <(while shuf -r -i1000000-9999999; do :; done)

# generate a file descriptor that emits an endless stream of UUIDs
exec {uuid_fd}< <(uuid_stream)

generate_mock_data() {
  typeset val
  case $1 in
    auto-increment)  val="$2" ;;
    uuid)            IFS= read -r val <&"$uuid_fd" || exit;;
    lat)             IFS= read -r val <&"$large_int_fd" || exit
                     val="$((RANDOM % 180 - 90)).$val" ;;
    long)            IFS= read -r val <&"$large_int_fd" || exit
                     val="$((RANDOM % 360 - 180)).$val" ;;
    ip)              val="$((RANDOM%256)).$((RANDOM%256)).$((RANDOM%256)).$((RANDOM%256))" ;; 
  esac
  printf '%s' "$val"
}

for ((i=0; i<2500; i++)); do                                             
  for header in auto-increment uuid lat long ip; do
    generate_mock_data "$header" "$i"
    printf ,
  done
  echo
done > file.csv

有什么不同?

  • 内部循环内没有命令替换。这意味着我们永远不会使用$()或任何同义词。其中每一项都涉及fork() - 创建一个新的操作系统级别的流程副本 - 以及一个wait(),带有一堆FIFO魔法来捕获我们的输出。
  • 内部循环内部没有外部命令。任何外部命令都比命令替换更糟糕:它们需要一个fork,然后另外需要一个{{ 1}},调用动态链接器和加载器,以便为正在运行的任何外部命令提取所有库依赖项。
  • 因为我们没有剥离换行符的命令替换,所以我们的功能就是不发出它们。