【问题标题】:bash separate parameters with specific delimiterbash 使用特定分隔符分隔参数
【发布时间】:2016-01-14 23:43:28
【问题描述】:

我正在搜索一个命令,它使用特定的分隔符分隔所有给定的参数,并将它们引用输出。

示例(分隔符设置为冒号:):

somecommand "this is" "a" test

应该输出

"this is":"a":"test"

我知道 shell 在将参数传递给命令之前会解释 "" 引号。所以这个命令实际上应该做的是打印出引号中的每个给定参数,并用冒号分隔所有这些。

我也不是寻求仅 bash 的解决方案,而是寻求最优雅的解决方案。
循环遍历这些元素的数组并执行此操作非常容易,但问题是我必须在 gnu makefile 中使用它,它只允许 单行 shell 命令 并使用 @987654325 @ 而不是 bash

所以越简单越好。

【问题讨论】:

  • “循环很容易” - 那你为什么不这样做呢?
  • 因为在 gnu makefile 中我只能有单行语句,而且它们只会在 shell 而不是 bash 中执行。
  • 通过“引用它们的输出”——引用的目标到底是什么?是否打算成为eval-safe 引用?它应该如何处理数据中的文字引号字符?什么构成正确的引用取决于什么软件将解析给定的字符串,我不相信这个问题具体说明了这一点。
  • 顺便说一句,你确实意识到你可以从你的makefile控制使用哪个shell,对吧?
  • 非常非常清楚——这里的引用不是eval-safe(也不会简单地替换单引号)。如果您在参数中有'$(rm -rf ~)',它将在双引号中发出,并在评估时执行;而$'\'$(rm -rf ~)\'' 将是一个能够转义单引号等的版本。如果你想生成 eval 安全的引用,那是 printf %q 的工作(而生成有效的 JSON 引用是 jq 的工作,生成 Python-安全引用是 python -c 'import sys; print repr(sys.argv[1])' 等的工作。

标签: linux bash shell makefile gnu-make


【解决方案1】:

怎么样

somecommand () {
    printf '"%s"\n' "$@" | paste -s -d :
}

使用printf 添加引号并将每个条目打印在单独的行上,然后使用paste-s(“串行”)选项和冒号作为分隔符。

可以这样调用:

$ somecommand "this is" "a" test
"this is":"a":"test"

【讨论】:

  • 嗯。您几乎可以使用printf ':"%s"' 并修剪第一个字符——或者将: 放在末尾并修剪最后一个字符。如果 shell 是 bash 而不是 sh(默认情况下由 makefile 使用),则不仅可以避免管道,甚至可以避免子 shell。
  • 正是我想到的那种解决方案,谢谢。 @CharlesDuffy 这确实是可能的,因为结果命令不关心尾随命令。但是 printf 的这个特性叫什么?没听说过。
  • @bricklore, ...如果有多余的参数,是否支持重复格式字符串?我不确定它的名称是否更短。 :)
【解决方案2】:
apply_delimiter () { 
    (( $# )) || return
    local res
    printf -v res '"%s":' "$@"
    printf '%s\n' "${res%:}"
}

使用示例:

$ apply_delimiter hello world "how are you"
"hello":"world":"how are you"

【讨论】:

    【解决方案3】:

    正如许多 cmets 所指出的,一个简单的“loop-over”方法,循环遍历作为参数传递的每个字符串是一种相当直接的方法:

    delimit_colon() {
        local first=1
        for i in "$@"; do
            if [ "$first" -eq 1 ]; then
                printf "%s" "$i"
                first=0
            else
                printf ":%s" "$i"
            fi
        done
        printf "\n"
    }
    

    结合简短的测试脚本可能是:

    #!/bin/bash
    
    delimit_colon() {
        local first=1
        for i in "$@"; do
            if [ "$first" -eq 1 ]; then
                printf "%s" "$i"
                first=0
            else
                printf ":%s" "$i"
            fi
        done
        printf "\n"
    }
    
    [ -z "$1" ] && {  ## validate input
        printf "error: insufficient input\n"
        exit 1
    }
    
    delimit_colon "$@"
    
    exit 0
    

    测试输入/输出

    $ bash delimitargs.sh "this is" "a" test
    this is:a:test
    

    【讨论】:

      【解决方案4】:

      这里是使用 z-shell 的解决方案:

      #!/usr/bin/zsh
      # this is "somecommand"
      echo '"'${(j_":"_)@}'"'
      

      【讨论】:

        【解决方案5】:

        如果您已经将它们放在数组中,则可以使用此命令

        MYARRAY=("this is" "a" "test")
        joined_string=$(IFS=:; echo "$(MYARRAY[*])")
        echo $joined_string
        

        设置 IFS(内部字段分隔符)将是字符分隔符。在阵列上使用 echo 将使用新设置的 IFS 显示阵列。将这些命令放入 $() 会将回显的输出放入joined_string。

        【讨论】:

        • 为了使其正确适用于该问题,我建议使用$* 而不是${MYARRAY[*]}(因为它专门指的是参数)。此外,您可能希望引用用作 echo 的参数的扩展。
        猜你喜欢
        • 1970-01-01
        • 2014-08-29
        • 2011-06-11
        • 1970-01-01
        • 1970-01-01
        • 2013-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多