linux 如何在打印bash脚本的参数时保留引号

xqk2d5yq  于 2023-08-03  发布在  Linux
关注(0)|答案(8)|浏览(166)

我正在编写一个bash脚本,它将打印并将复杂的参数传递给另一个外部程序。

./script -m root@hostname,root@hostname -o -q -- 'uptime ; uname -a'

字符串
如何打印原始参数:

-m root@hostname,root@hostname -o -q -- 'uptime ; uname -a'


使用$@$*删除了uptime ; uname -a周围的单引号,这可能会导致不希望的结果。我的脚本不需要解析每个参数。我只需要打印/记录参数字符串,并将它们传递给另一个程序。
我知道我可以用类似"'uptime ; uname -a'"的东西来转义引号,但我不能保证用户会这样做。

ccgok5k5

ccgok5k51#

引号在参数传递到脚本之前就被删除了,所以现在要保留它们已经太晚了。您可以做的是在将参数传递给内部命令时保留它们的效果,并重新构造参数的等效引号/转义版本以进行打印。
为了将参数传递给内部命令"$@"(带双引号),$@保留了原始的单词分隔符,这意味着内部命令接收的参数列表与脚本接收的参数列表完全相同。
对于打印,可以在bash的printf命令中使用%q格式来重新构造引号。请注意,这并不总是重构原始的引号,而是构造一个 * 等效 * 的引号/转义字符串。例如,如果你传递参数'uptime ; uname -a',它可能会打印uptime\ \;\ uname\ -a"uptime ; uname -a"或任何其他等效的值(类似的例子见@William Pursell的答案)。
下面是一个使用这些的示例:

printf "Running command:"
printf " %q" innercmd "$@" # note the space before %q -- this inserts spaces between arguments
printf "\n"
innercmd "$@"

字符串
如果您有bash 4.4或更高版本,您可以在参数展开上使用@Q修饰符来添加引号。这倾向于使用单引号(而不是printf %q对转义的偏好)。您可以将其与$*结合使用,以获得合理的结果:

echo "Running command: innercmd ${*@Q}"
innercmd "$@"


请注意,$*将所有参数混合到一个字符串中,它们之间有空格,这通常是没有用的,但在这种情况下,每个参数都被单独引用,因此结果实际上是您(可能)想要的。(好吧,除非你改变了IFS,在这种情况下,参数之间的“空格”将是$IFS的第一个字符,这可能不是你想要的。

i1icjdpr

i1icjdpr2#

使用${@@Q}可获得简单的解决方案。为了测试,将下面的行放在脚本bigQ中。

#!/bin/bash
line="${@@Q}"
echo $line

./bigQ 1 a "4 5" b="6 7 8"
'1' 'a' '4 5' 'b=6 7 8'

字符串

llew8vvj

llew8vvj3#

如果用户调用您的命令为:

./script 'foo'

字符串
给脚本的第一个参数是不带引号的字符串foo。你的脚本没有办法区分它和任何其他可以获取foo作为参数的方法(例如./script $(echo foo)./script foo./script "foo"./script \f\o""''""o)。

46scxncf

46scxncf4#

如果要打印尽可能接近用户可能输入内容的参数列表:

#!/bin/bash
chars='[ !"#$&()*,;<>?\^`{|}]'
for arg
do
    if [[ $arg == *"'"* ]]
    then
        arg=\""$arg"\"
    elif [[ $arg == *$chars* ]]
    then
        arg="'$arg'"
    fi
    allargs+=("$arg")    # ${allargs[@]} is to be used only for printing
done
printf '%s\n' "${allargs[*]}"

字符串
它并不完美。像''\''"'这样的论证比合理的论证更难适应。

ryhaxcpt

ryhaxcpt5#

正如其他人已经提到的,当您访问脚本中的参数时,要知道调用时引用了哪些参数已经为时已晚。但是,您可以对包含空格或其他特殊字符的参数重新加引号,这些字符需要加引号才能作为参数传递。
下面是一个基于Python的shlex.quote(s)的Bash实现,它可以做到这一点:

function quote() {
  declare -a params
  for param; do
    if [[ -z "${param}" || "${param}" =~ [^A-Za-z0-9_@%+=:,./-] ]]; then
      params+=("'${param//\'/\'\"\'\"\'}'")
    else
      params+=("${param}")
    fi
  done
  echo "${params[*]}"
}

字符串
您的示例稍微更改为显示空参数:

$ quote -m root@hostname,root@hostname -o -q -- 'uptime ; uname -a' ''
-m root@hostname,root@hostname -o -q -- 'uptime ; uname -a' ''

p8h8hvxi

p8h8hvxi6#

在我的例子中,我尝试调用bash,如script --argument="--arg-inner=1 --arg-inner2"。不幸的是,任何解决方案上不帮助我的情况。
最终解决方案为

#!/bin/bash
# Fix given array argument quotation
function quote() {
    local QUOTED_ARRAY=()
    for ARGUMENT; do
        case ${ARGUMENT} in
            --*=*)
                QUOTED_ARRAY+=( "${ARGUMENT%%=*}=$(printf "%q" "${ARGUMENT#*=}")" )
                shift
            ;;
            *)
                QUOTED_ARRAY+=( "$(printf " %q" "${ARGUMENT}")" )
            ;;
        esac
    done
    echo ${QUOTED_ARRAY[@]}
}

ARGUMENTS="$(quote "${@}")"

echo "${ARGUMENTS}"

字符串
在MacOS的情况下,结果是--argument=--arg-inner=1\ --arg-inner2,这在逻辑上是相同的。

qeeaahzv

qeeaahzv7#

我有一个非常简单的解决方案,也适用于sh shell:
script1.sh

#!/bin/sh

concatenated_string=""

for ARG in "$@"; do
    concatenated_string="${concatenated_string}'${ARG}' "
done

echo "${concatenated_string}"
eval "${concatenated_string}"

字符串
当我调用script1.sh时:./script1.sh sh -c "serve -l 9527 -s dist"
产出将是:

'sh' '-c' 'serve -l 9527 -s dist' 

   ┌───────────────────────────────────────────────────┐
   │                                                   │
   │   Serving!                                        │
   │                                                   │
   │   - Local:            http://localhost:9527       │
   │   - On Your Network:  http://192.168.2.102:9527   │
   │                                                   │
   └───────────────────────────────────────────────────┘


如您所见,第一行回显保留引号的字符串;第二行通过使用eval直接执行sh -c "serve -l 9527 -s dist"。请注意,您可以将任何想要评估的命令作为script1.sh的参数传递。

ubby3x7f

ubby3x7f8#

只需使用引号nul字符分隔每个参数:

#! /bin/bash

sender () {
    printf '"%s"\0' "$@"
}

receiver () {
    readarray -d '' args < <(function "$@")
}

receiver "$@"

字符串
Charles Duffy所述。

相关问题