V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
mylovesaber
V2EX  ›  Linux

给精简的 shell 循环遍历代码只传两个参数,处理时间差距 10 倍是为何?内详

  •  
  •   mylovesaber · 2022-07-12 22:25:45 +08:00 · 3452 次点击
    这是一个创建于 893 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我今天在写一个 ssh 免密脚本,然后发现执行效率极低,换了种写法,发现一秒以内运行完成,添加提示信息发现了一个没想明白的情况,我提炼了具体情况,还请各位老哥解个惑

    精简的代码贴上:

    #!/bin/bash
    declare -a EXTRAARG=("$@")
    
    MySQLKeywords=(accessible account action add after against aggregate algorithm all alter always analyze and any as asc ascii asensitive at autoextend_size auto_increment avg avg_row_length backup before begin between bigint binary binlog bit blob block bool boolean both btree by byte cache call cascade cascaded case catalog_name chain change changed channel char character charset check checksum cipher class_origin client close coalesce code collate collation column columns column_format column_name comment commit committed compact completion component compressed compression concurrent condition connection consistent constraint constraint_catalog constraint_name constraint_schema contains context continue convert cpu create cross current current_date current_time current_timestamp current_user cursor cursor_name data database databases datafile date datetime day day_hour day_microsecond day_minute day_second deallocate dec decimal declare default default_auth definer delayed delay_key_write delete desc describe deterministic diagnostics directory disable discard disk distinct distinctrow div do double drop dual dumpfile duplicate dynamic each else elseif enable enclosed encryption end ends engine engines enum error errors escape escaped event events every except exchange execute exists exit expansion expire explain export extended extent_size false fast faults fetch fields file file_block_size filter first fixed float float4 float8 flush follows for force foreign format found from full fulltext general generated geometry geometrycollection get get_format global grant grants group group_replication handler hash having help high_priority host hosts hour hour_microsecond hour_minute hour_second identified if ignore ignore_server_ids import in index indexes infile initial_size inner inout insensitive insert insert_method install instance int int1 int2 int3 int4 int8 integer interval into invisible invoker io io_after_gtids io_before_gtids io_thread ipc is isolation issuer iterate join json key keys key_block_size kill language last leading leave leaves left less level like limit linear lines linestring list load local localtime localtimestamp lock locks logfile logs long longblob longtext loop low_priority master master_auto_position master_bind master_connect_retry master_delay master_heartbeat_period master_host master_log_file master_log_pos master_password master_port master_retry_count master_ssl master_ssl_ca master_ssl_capath master_ssl_cert master_ssl_cipher master_ssl_crl master_ssl_crlpath master_ssl_key master_ssl_verify_server_cert master_tls_version master_user match maxvalue max_connections_per_hour max_queries_per_hour max_rows max_size max_updates_per_hour max_user_connections medium mediumblob mediumint mediumtext memory merge message_text microsecond middleint migrate minute minute_microsecond minute_second min_rows mod mode modifies modify month multilinestring multipoint multipolygon mutex mysql_errno name names national natural nchar ndb ndbcluster never new next no nodegroup none not no_wait no_write_to_binlog null number numeric nvarchar offset on one only open optimize optimizer_costs option optionally options or order out outer outfile owner pack_keys page parser partial partition partitioning partitions password phase plugin plugins plugin_dir point polygon port precedes precision prepare preserve prev primary privileges procedure processlist profile profiles proxy purge quarter query quick range read reads read_only read_write real rebuild recover redo_buffer_size redundant references regexp relay relaylog relay_log_file relay_log_pos relay_thread release reload remove rename reorganize repair repeat repeatable replace replicate_do_db replicate_do_table replicate_ignore_db replicate_ignore_table replicate_rewrite_db replicate_wild_do_table replicate_wild_ignore_table replication require reset resignal restore restrict resume return returned_sqlstate returns reverse revoke right rlike rollback rollup rotate routine row_count row_format rtree savepoint schedule schema schemas schema_name second second_microsecond security select sensitive separator serial serializable server session set share show shutdown signal signed simple slave slow smallint snapshot socket some soname sounds source spatial specific sql sqlexception sqlstate sqlwarning sql_after_gtids sql_after_mts_gaps sql_before_gtids sql_big_result sql_buffer_result sql_calc_found_rows sql_no_cache sql_small_result sql_thread sql_tsi_day sql_tsi_hour sql_tsi_minute sql_tsi_month sql_tsi_quarter sql_tsi_second sql_tsi_week sql_tsi_year ssl stacked start starting starts stats_auto_recalc stats_persistent stats_sample_pages status stop storage stored straight_join string subclass_origin subject subpartition subpartitions super suspend swaps switches table tables tablespace table_checksum table_name temporary temptable terminated text than then time timestamp timestampadd timestampdiff tinyblob tinyint tinytext to trailing transaction trigger triggers true truncate type types uncommitted undefined undo undofile undo_buffer_size unicode uninstall union unique unknown unlock unsigned until update upgrade usage use user user_resources use_frm using utc_date utc_time utc_timestamp validation value values varbinary varchar varcharacter variables varying view virtual visible wait warnings week weight_string when where while with without work wrapper write x509 xa xid xml xor year year_month zerofill zone)
    
    
    for GROUP_NAME in "${EXTRAARG[@]}";do
        GROUP_NAME=$(echo "${GROUP_NAME}"|tr "[:upper:]" "[:lower:]")
        # echo "tr 转换的数组元素: ${GROUP_NAME}"
    
        # 0.7s 一个纯字母,一个字母中有一个冒号
        if [[ "${GROUP_NAME}" =~ ":" ]]; then
            GROUP_NAME1=$(echo "${GROUP_NAME}"|cut -d':' -f 1)
            GROUP_NAME2=$(echo "${GROUP_NAME}"|cut -d':' -f 2)
            if [ -n "$(echo "${GROUP_NAME}"|cut -d':' -f 3)" ];then
                _error "非法字符: $(echo "${GROUP_NAME}"|cut -d':' -f 3)" && exit 1
            else
                for j in "${MySQLKeywords[@]}";do
                    if [ "${GROUP_NAME1}" = "$j" ] || [ "${GROUP_NAME2}" = "$j" ];then
                        _error "参数不能为 MySQL 的关键字或保留字!退出中" && exit 1
                    fi
                done
            fi
        elif [[ ! "${GROUP_NAME}" =~ ":" ]]; then
            for j in "${MySQLKeywords[@]}";do
                    if [ "${GROUP_NAME}" = "$j" ];then
                        _error "参数不能为 MySQL 的关键字或保留字!退出中" && exit 1
                    fi
            done
        fi
    
    
        # 7s 一个纯字母,一个字母中有一个冒号
        for j in "${MySQLKeywords[@]}";do
            if [[ "${GROUP_NAME}" =~ ":" ]]; then
                GROUP_NAME1=$(echo "${GROUP_NAME}"|cut -d':' -f 1)
                GROUP_NAME2=$(echo "${GROUP_NAME}"|cut -d':' -f 2)
                if [ -n "$(echo "${GROUP_NAME}"|cut -d':' -f 3)" ];then
                    _error "非法字符: $(echo "${GROUP_NAME}"|cut -d':' -f 3)" && exit 1
                elif [ "${GROUP_NAME1}" = "$j" ] || [ "${GROUP_NAME2}" = "$j" ];then
                    _error "参数不能为 MySQL 的关键字或保留字!退出中" && exit 1
                fi
            elif [[ ! "${GROUP_NAME}" =~ ":" ]]; then
                if [ "${GROUP_NAME}" = "$j" ];then
                    _error "参数不能为 MySQL 的关键字或保留字!退出中" && exit 1
                fi
            fi
        done
        
        
        
    done
    

    脚本功能

    1. 以上脚本声明了两个数组变量,一个是从用户输入获取的参数,另一个相当长的是 mysql 的默认关键字和保留字
    2. 脚本读取并遍历用户输入的信息,每一个信息都进 mysql 关键字数组遍历对比,发现是 mysql 保留字就报错退出。
    3. 脚本中有两种循环写法,运行时间差距极大,对比测试运行时需要注释掉一组

    脚本流程

    用户给脚本提供两种格式的变量,一个是纯大小写字母,一个是字母中有一个英文冒号分隔符,但只能有一个冒号存在,即之后利用冒号进行变量字符的拆分的时候,最多只会拆成两个字符串。

    我输入了两个变量:asdf 和 asd:f

    后文加粗代表操作相同 简单来说,同样有外部遍历循环套着,区别在于:

    • 龟速的是内部用遍历套着一组 if 判断
    • 高速的是一组 if 判断各自套着一组内部遍历

    7 秒龟速循环流程

    采用循环内使用 if 进行分支判断

    1. 获取两个变量的数组后对两个变量进行遍历,为外部循环
    2. 每获取一个外部循环进来的变量就立即进入对 sql 关键字数组遍历的内部循环
    3. 对外部循环进来的单个变量检查是否包含冒号
    4. 如果有冒号,就根据冒号进行分割成两个字符串
    5. 检测按冒号分割,会不会有第三个非空字符串,如果存在,则报错退出
    6. 将拆分出来的两个字符串分别对比关键字数组遍历时的那轮关键字,如果任意一个字符串与该轮循环的 sql 关键字相同则报错退出,到此带冒号的遍历流程完毕
    7. 如果没冒号,则将变量直接对比关键字数组遍历时的那轮关键字,如果俩字符串相同则报错退出

    0.7 秒高速循环流程

    采用 if 进行分支判断后送入各自的循环

    1. 获取两个变量的数组后对两个变量进行遍历,为外部循环
    2. 每获取一个外部循环进来的变量就检查是否包含冒号
    3. 如果有冒号,就根据冒号进行分割成两个字符串
    4. 检测按冒号分割,会不会有第三个非空字符串,如果存在,则报错退出
    5. 否则进入对 sql 关键字数组遍历的内部循环
    6. 将拆分出来的两个字符串分别对比关键字数组遍历时的那轮关键字,如果拆分出来的两个字符串中任意一个与该轮循环的 sql 关键字相同则报错退出,到此带冒号的遍历完毕
    7. 如果没冒号,则将变量直接对关键字数组遍历,如果数组中存在相同字符串则报错退出
    30 条回复    2022-07-23 22:46:13 +08:00
    xuboying
        1
    xuboying  
       2022-07-12 22:44:10 +08:00
    这两种写法差异太大了把。
    最大的差异在于第二种你反复的用了 cut echo 等系统调用处理重复的内容。这些系统调用开销巨大。
    无论你用那种编程语言写。第二种的浪费的时间就和你的 sql 关键字数量成正相关。
    Jooooooooo
        2
    Jooooooooo  
       2022-07-12 22:45:15 +08:00
    没看完, 全凭猜测:

    一个可能是: 对单个数组顺序遍历元素, 数组里的元素一般会放在相邻的内存上, cpu 访问起来就会更快(因为加载内存里 1 个元素到 cpu 里的时候, 很可能后面几个元素也一起加载过来了)

    另外一个可能是: 搜索 branch misprediction
    aheadlead
        3
    aheadlead  
       2022-07-12 22:53:41 +08:00
    for-loop 会 fork subshell... 所以肯定会慢
    aheadlead
        4
    aheadlead  
       2022-07-12 22:55:20 +08:00
    代码没仔细看,建议可以用 associated map 当作 set 用,应该速度会快得多
    aheadlead
        5
    aheadlead  
       2022-07-12 22:57:42 +08:00
    shell 是一种很一言难尽的语言 有它特别好用的地方 但学起来比较痛苦
    尽量不要在里面跑太多重 cpu 的逻辑…
    wxf666
        6
    wxf666  
       2022-07-13 03:43:24 +08:00   ❤️ 11
    主要是你启动的进程太多,又叠加循环大量关键字了($() 会开启 sub shell ,你还有一堆 echo 、cut )

    对于 7s 部分的代码,每测试一个含 : 的参数,你就要开启 3 + 614 * 9 = 5529 次子进程。。。


    ●针对 7s 部分,以一个参数 'asd:f' 逐步进行测试:

    1. 使用 <<<xxx 优化掉 echo xxx ,用时为原来的 74%(即 5.2 秒)

    GROUP_NAME1=$(cut -d':' -f 1 <<<"$GROUP_NAME")
    GROUP_NAME2=$(cut -d':' -f 2 <<<"$GROUP_NAME")
    if [ -n "$(cut -d':' -f 3 <<<"$GROUP_NAME")" ]; then

    2. 使用 bash 内置的替换功能,来代替 cut ,用时为原来的 30%(即 2.1 秒)

    GROUP_NAME1=$(echo "${GROUP_NAME%%:*}") # 删掉 :.*$
    GROUP_NAME2=$(echo "${GROUP_NAME##*:}") # 删掉 ^.*:
    if ([[ $GROUP_NAME =~ ^.*:.*: ]]); then  # () 用来开启一次 sub shell

    3. 取消掉 $(),用时为原来的 1%(即 0.07 秒)

    GROUP_NAME1=${GROUP_NAME%%:*} # 删掉 :.*$
    GROUP_NAME2=${GROUP_NAME##*:} # 删掉 ^.*:
    if [[ $GROUP_NAME =~ ^.*:.*: ]]; then
      _error "非法字符: ${GROUP_NAME#*:*:}"; exit 1


    ●另外,bash 也内置了大小写转换功能:

    declare -a EXTRAARG=("${@,,}") # 将每个参数的每个字符转为小写
    # GROUP_NAME=$(echo "${GROUP_NAME}"|tr "[:upper:]" "[:lower:]") # 去掉这行


    ●还可考虑使用关联数组,用时为原来的 0.00071%(原 7 秒 可执行 140000 次)

    declare -A dict="($(printf "['%s']= " "${MySQLKeywords[@]}"))" # 要求关键字里没有 ' 字符,否则老老实实 for

    for GROUP_NAME in "${EXTRAARG[@]}";do

      # 每个参数循环多次测试
       for ((i = 0; i < 140000; i++)); do
        # 删掉 :
         GROUP_NO_SEP=${GROUP_NAME//:}

        # 计算 : 个数(原字符串长度 - 删掉 : 后长度)
         case $(( ${#GROUP_NAME} - ${#GROUP_NO_SEP} )) in
           0)
            [[ -v dict[$GROUP_NAME] ]] || continue
            ;;
           1)
            [[ -v dict[${GROUP_NAME%%:*}] ||
              -v dict[${GROUP_NAME##*:}] ]] || continue
            ;;
          *)
            _error "非法字符: ':${GROUP_NAME#*:*:}'"
             exit 1
            ;;
         esac

        # 出现了关键字
        _error "参数不能为 MySQL 的关键字或保留字!退出中"
         exit 1
       done
    done


    ●或者使用 awk 里的数组,用时为原来的 0.0004%(原 7 秒 可执行 250000 次)

    for GROUP_NAME in "${EXTRAARG[@]}";do

      # 每个参数循环多次测试
       for ((i = 0; i < 250000; i++)); do
         echo "$GROUP_NAME"
       done

    done | awk -F: '
       BEGIN {
        # 要求关键字里不出现 : " 等字符
         split("'"$(IFS=:; echo "${MySQLKeywords[*]}")"'", arr)
         for (i in arr) dict[arr[i]] = 1;
      }

      {
         if (NF > 2) {
           print "非法字符: " $3
           exit 1
        }

         for (i = 1; i <= NF; i++) {
           if ($i in dict) {
             print "参数不能为 MySQL 的关键字或保留字!退出中"
             exit 1
          }
        }
      }
    '
    haoliang
        7
    haoliang  
       2022-07-13 04:21:37 +08:00
    对于楼上回答的补充:
    * bash 的文本操作很难用是事实,所以用外部程序替换来快速实现功能没毛病; 2/8 原则,我觉得没必要搁这死磕
    * echo 是 shell built-in command , `echo xx | cmd` 的主要开销主要在这个 pipeline 上;
    * 很显然重复使用的 group_name* 需要提出来;

    对于更早楼层的补充:
    * bash 中 for, while 的每次执行不会创建 subshell (可以通过 subshell 中不能修改父进程中变量的值来验证)
    wxf666
        8
    wxf666  
       2022-07-13 04:33:21 +08:00
    @haoliang 请教一下,`echo xx | cmd` 的主要开销如果 pipeline 上的话,<<< 的实现是啥?不是管道传输作为标准输入吗?

    为嘛 v 站的回复不能用 markdown 。。
    haoliang
        9
    haoliang  
       2022-07-13 06:40:41 +08:00
    @wxf666 写回复的时候,我以为`<<<`是直接写入到 cmd 的 stdin ,不过没求证
    haoliang
        10
    haoliang  
       2022-07-13 06:51:11 +08:00
    “请教”言重了,我可能有错。pipeline 跟 redirect 的差别,我觉得这个[佐证])( https://stackoverflow.com/questions/9553628/piping-and-redirection)应该没问题
    rrfeng
        11
    rrfeng  
       2022-07-13 08:58:10 +08:00 via Android
    单纯对比来讲
    明显第二种写法循环次数多啊,每个底层循环的操作是一样的。
    不然你把脚本传入的参数增加到和内置列表一样多看看,时间就会差不多了。

    从优化来讲上面同学已经讲的很详细了,shell 真的是跟比 js 还充满了各种奇技淫巧,并且大概率你不知道某些写法。学不完的…
    这脚本用 Python 写个可能更合适。
    aloxaf
        12
    aloxaf  
       2022-07-13 10:15:30 +08:00
    少年哟,要不要来考虑使用 zsh 呢? bash 实在太难用了,如非必要,我基本上不会碰它……

    https://fars.ee/gWnf/sh
    merlin852
        13
    merlin852  
       2022-07-13 11:24:38 +08:00
    上面大部分都是程序员思维,shell 思维的写法是:
    if echo $MySQLKeywords |grep -q xxx ; then yes ; else no ; fi
    rev1si0n
        14
    rev1si0n  
       2022-07-13 11:54:44 +08:00
    @aloxaf 事实上,你不能让人跑你的脚本还专门装个 zsh ,但是 sh 和 bash 基本都有
    wxf666
        15
    wxf666  
       2022-07-13 13:16:10 +08:00
    @haoliang 试了一下,<<< 和 <<EOF 都实现为 pipe ,< file 则不是

    bash -c '[[ -p /dev/stdin ]] && echo piped' <a.txt # 没回应

    echo | bash -c '[[ -p /dev/stdin ]] && echo piped' # piped
    bash -c '[[ -p /dev/stdin ]] && echo piped' < <(echo 'abc') # piped
    bash -c '[[ -p /dev/stdin ]] && echo piped' <<<'abc' # piped
    bash -c '[[ -p /dev/stdin ]] && echo piped' <<EOF # piped
    abc
    EOF

    所以应该不是管道导致的慢(大家都用的管道)

    也试了下,在 $() 中确实没见到 echo 等 builtin 命令的进程,如:$(read -t 3 | sleep 3)

    估计是由 $() 实现了 read ?没启动外部进程,但内部还是执行了相关功能?(如 echo -e 会转义参数里的 \ 内容)

    所以 echo | xxx 比 <<< 慢?
    aloxaf
        16
    aloxaf  
       2022-07-13 14:47:45 +08:00
    @rev1si0n 给别人那肯定还是 bash ,但自己用的时候就没必要折磨自己了

    @wxf666
    管道和 $() 都会启动一个 subshell ,而 <<< 只是单纯的重定向

    https://man.archlinux.org/man/bash.1#Pipelines
    https://man.archlinux.org/man/bash.1#Command_Substitution
    xuboying
        17
    xuboying  
       2022-07-13 15:28:01 +08:00
    @merlin852 #13 难道不应该是 echo xxx | grep -q xxx && yes || no?
    mylovesaber
        18
    mylovesaber  
    OP
       2022-07-13 16:17:21 +08:00
    @aloxaf 保密局用的全是 bash ,zsh 没有装,所有设备都是内网模式,dnf 或者 yum 都不具备随意安装软件的功能,安装的软件也得有专门授权处理才能装,我运行这个脚本都只能通过 `bash <(cat xxx.sh) --help` 这种写法来实现... orz 不知道有没有别的更好的办法能运行脚本
    mylovesaber
        19
    mylovesaber  
    OP
       2022-07-13 16:24:27 +08:00
    @rrfeng 脚本在涉密机器用的,python 最大问题就是依赖,那些服务器不能联网,python 只有 2.7 ,除了首次部署装的包,其他应该都没有,自己也装不了,yum/dnf 安装的包需要鉴权,否则装不上,不知道 python 的有没有类似限制,这种情况下还得靠 shell 脚本,而且写之前先得去模拟机上查一下有没有相关命令
    wxf666
        20
    wxf666  
       2022-07-13 16:25:23 +08:00
    @aloxaf 嗯,$() 会启动 sub shell ,但 | 只是保证各个命令会在独立进程中执行吧

    > Each command in a pipeline is executed as a separate process (i.e., in a subshell)

    1. 如 sleep 2 | sleep 3:

    /init
    └─ -bash
      ├─ sleep 2
      └─ sleep 3

    2. 而 f() { sleep 2; }; f | sleep 3:

    /init
    └─ -bash
      ├─ -bash
      │ └─ sleep 2
      └─ sleep 3

    至于你说的“<<< 只是单纯的重定向”,是其 stdin 重定向至(临时)文件?还是管道(由某个 bash 喂进去)?
    mylovesaber
        21
    mylovesaber  
    OP
       2022-07-13 16:28:30 +08:00
    @wxf666 非常感谢大佬的教程,我先琢磨下先
    wxf666
        22
    wxf666  
       2022-07-13 16:46:38 +08:00
    @mylovesaber 不是大佬,只是被 bash 折磨过一阵

    原文有些错误,后面也有人纠正了,注意甄别

    1. 对于 7s 部分的代码,每测试一个含 : 的参数,你就要开启 2 + 614 * 6 = 3686 次子进程。。。

    2. awk 的初始化部分,应该在外部传递参数。如 -v keywords="$(IFS=:; echo "${MySQLKeywords[*]}")"

    不能 xxx.sh 是因为没有执行权限?好奇你 bash xxx.sh 会发生啥
    haoliang
        23
    haoliang  
       2022-07-13 17:06:15 +08:00
    @wxf666 我 strace 看了 bash 下两种写法,确实都调用 pipe2 ,差别在输入的长度不够 64k 时,`|` 多创建了个进程写入数据到 pipe; 超过 64k 时,我没看 `<<<str` 的 strace log ,毕竟这个数量的数据不像是直接字符串形式存在脚本里的;如果用 <file 、 <(cat file),我日等等,写个脚本还得深入到 syscall 级别,我觉得意义不大,这块我也不熟。等个较真的大佬吧

    我还写了个 benchmark 针对这种情况分别给 sh 、bash 、zsh ,其中 sh 的 pipeline 比 redirect 快 64%,不过 cpu 占用高 31%;其他两个 shell 中 redirect 比 pipeline 高效。

    ```
    # sh pipeline*100
    0.10user 0.09system 0:00.14elapsed 136%CPU (0avgtext+0avgdata 4208maxresident)k
    0inputs+0outputs (0major+22025minor)pagefaults 0swaps
    # sh redirect*100
    0.13user 0.10system 0:00.22elapsed 104%CPU (0avgtext+0avgdata 4152maxresident)k
    0inputs+0outputs (0major+13876minor)pagefaults 0swaps
    # bash pipeline*100
    0.19user 0.16system 0:00.27elapsed 132%CPU (0avgtext+0avgdata 4236maxresident)k
    0inputs+0outputs (0major+21421minor)pagefaults 0swaps
    # bash redirect*100
    0.11user 0.12system 0:00.22elapsed 104%CPU (0avgtext+0avgdata 4164maxresident)k
    0inputs+0outputs (0major+13683minor)pagefaults 0swaps
    # zsh pipeline*100
    0.17user 0.17system 0:00.29elapsed 120%CPU (0avgtext+0avgdata 3992maxresident)k
    0inputs+0outputs (0major+20326minor)pagefaults 0swaps
    # zsh redirect*100
    0.11user 0.14system 0:00.24elapsed 105%CPU (0avgtext+0avgdata 4020maxresident)k
    0inputs+0outputs (0major+14989minor)pagefaults 0swaps
    ```

    用到的 makefile

    ```

    strace:
    # bash
    @ strace -f bash -c 'echo "abc:def" | /usr/bin/cut -d: -f 1 >/dev/null' 2> bash.pipeline.strace
    @ strace -f bash -c '/usr/bin/cut -d: -f 1 <<<"abc:def" >/dev/null' 2> bash.redirect.strace
    # sh
    @ strace -f sh -c 'echo "abc:def" | /usr/bin/cut -d: -f 1 >/dev/null' 2> sh.pipeline.strace
    @ strace -f sh -c '/usr/bin/cut -d: -f 1 <<<"abc:def" >/dev/null' 2> sh.redirect.strace

    benchmark:
    # sh pipeline*100
    @ time sh -c 'for _ in {0..100}; do echo "abc:def" | /usr/bin/cut -d: -f 1 >/dev/null; done'
    # sh redirect*100
    @ time sh -c 'for _ in {0..100}; do /usr/bin/cut -d: -f 1 <<<"abc:def" >/dev/null; done'
    # bash pipeline*100
    @ time bash -c 'for _ in {0..100}; do echo "abc:def" | /usr/bin/cut -d: -f 1 >/dev/null; done'
    # bash redirect*100
    @ time bash -c 'for _ in {0..100}; do /usr/bin/cut -d: -f 1 <<<"abc:def" >/dev/null; done'
    # zsh pipeline*100
    @ time zsh -c 'for _ in {0..100}; do echo "abc:def" | /usr/bin/cut -d: -f 1 >/dev/null; done'
    # zsh redirect*100
    @ time zsh -c 'for _ in {0..100}; do /usr/bin/cut -d: -f 1 <<<"abc:def" >/dev/null; done'
    ```
    novolunt
        24
    novolunt  
       2022-07-13 17:13:59 +08:00
    烽火奇安信还是深信服
    lolizeppelin
        25
    lolizeppelin  
       2022-07-13 17:18:10 +08:00
    核心是减少 fork 和数据复制
    创建 pipe 本身有消耗,pipe 两端的数据又是通过复制传递

    python 不装包你把代码全传上去不就行了....包不就是个文件夹而已,不是非要装的

    你服务器上都能折腾这么乱起八糟的 shell 代码, 传几个文件反而不行么

    话说不让 dnf,但是能传文件就能 rpm -ivh 啊...
    mylovesaber
        26
    mylovesaber  
    OP
       2022-07-13 18:24:53 +08:00
    @lolizeppelin rpm 好像被禁了,之前看公司同事操作过,rsync 都装不上
    mylovesaber
        27
    mylovesaber  
    OP
       2022-07-13 18:25:56 +08:00
    @novolunt 都不是,和反贪腐有关,面向纪委的
    novolunt
        28
    novolunt  
       2022-07-14 13:50:56 +08:00
    @mylovesaber 就剩一家了 mybk 这家确实有做纪委的
    mylovesaber
        29
    mylovesaber  
    OP
       2022-07-14 16:40:15 +08:00
    @novolunt 不是这家,不用猜了,我就是来问个问题的,这情况以前没遇到过感觉有点奇怪 orz
    mylovesaber
        30
    mylovesaber  
    OP
       2022-07-23 22:46:13 +08:00
    @wxf666 直接 bash xx.sh 会提示 permission denied. 我运行脚本靠的是进程替换实现: bash < <(cat xx.sh),或者 cat xx.sh|bash 也行。生产机上奇怪的限制很多,比如说不登录进去靠启动 live 系统把密码改了,重新进正常系统密码恢复原样。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2863 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 12:47 · PVG 20:47 · LAX 04:47 · JFK 07:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.