我想要做的是,作为函数的输入,可以包含引号(单引号或双引号)的行,并且回显与提供给函数的行完全相同。例如:
function doit {
printf "%s " ${@}
eval "${@}"
printf " # [%3d]\n" ${?}
}
其中,给出以下输入
doit VAR=42
doit echo 'single quote $VAR'
doit echo "double quote $VAR"
产生以下内容:
VAR=42 # [ 0]
echo single quote $VAR # [ 0]
echo double quote 42 # [ 0]
因此我可以保留变量扩展的语义,但是我无法获得提供给函数的行的确切格式。我希望将doit echo 'single quote $VAR'
结果导入echo 'single quote $VAR'
。
我确信这与bash在传递给函数之前处理参数有关;我只是想找个方法(如果可能的话)。
所以我的目的是影响脚本的执行,同时提供执行的精确副本,可以用作诊断工具,包括每个步骤的退出状态。
虽然我可以通过执行类似
的操作获得上述所需的行为while read line ; do
doit ${line}
done < ${INPUT}
这种方法在控制结构面前失败(即if
,while
等)。我考虑过使用set -x
,但也存在限制:"
变为'
,退出状态对于失败的命令不可见。
答案 0 :(得分:10)
我处于类似的位置,因为我需要一个脚本来包装现有命令并传递保持引用的参数。
我想出的一些东西并没有完全按照键入的方式保留命令行,而是正确地传递了参数并告诉你它们是什么。
这是我的脚本设置为阴影ls
:
CMD=ls
PARAMS=""
for PARAM in "$@"
do
PARAMS="${PARAMS} \"${PARAM}\""
done
echo Running: ${CMD} ${PARAMS}
bash -c "${CMD} ${PARAMS}"
echo Exit Code: $?
这是一些示例输出:
$ ./shadow.sh missing-file "not a file"
Running: ls "missing-file" "not a file"
ls: missing-file: No such file or directory
ls: not a file: No such file or directory
Exit Code: 1
因为你可以看到它添加了原来不存在的引号,但它确实保留了带有空格的参数,而这正是我所需要的。
答案 1 :(得分:7)
发生这种情况的原因是因为bash正如您所想的那样解释参数。当它调用函数时,引号根本不再存在,所以这是不可能的。它在DOS下工作,因为程序可以自己解释命令行,而不是它可以帮助你!
答案 2 :(得分:4)
虽然@Peter Westlake&#39; answer是正确的,并且没有引号可以保留,但可以尝试推断出引用是否在需要的地方并因此最初传入。就个人而言,当我在日志中需要一个使用正确引用运行命令的证明时,我使用了这个requote
函数:
function requote() {
local res=""
for x in "${@}" ; do
# try to figure out if quoting was required for the $x:
grep -q "[[:space:]]" <<< "$x" && res="${res} '${x}'" || res="${res} ${x}"
done
# remove first space and print:
sed -e 's/^ //' <<< "${res}"
}
以下是我如何使用它:
CMD=$(requote "${@}")
# ...
echo "${CMD}"
答案 3 :(得分:3)
doit echo "'single quote $VAR'"
doit echo '"double quote $VAR"'
两者都有效。
bash只会在输入函数时删除外部引号集。
答案 4 :(得分:1)
当您使用quote in作为命令行参数传递字符串时,Bash将删除引号。当字符串传递给您的脚本时,引用就不再存在了。你无法知道有单引号或双引号。
你可能做的就是这样:
doit VAR=42
doit echo \'single quote $VAR\'
doit echo \"double quote $VAR\"
在你的脚本中你得到了
echo 'single quote $VAR'
echo "double quote $VAR"
或者这样做
doit VAR=42
doit echo 'single quote $VAR'
doit echo '"double quote $VAR"'
在你的脚本中你得到了
echo single quote $VAR
echo "double quote $VAR"
答案 5 :(得分:1)
此:
ponerApostrofes1 ()
{
for (( i=1; i<=$#; i++ ));
do
eval VAR="\${$i}";
echo \'"${VAR}"\';
done;
return;
}
例如,当参数有撇号时会出现问题。
此功能:
ponerApostrofes2 ()
{
for ((i=1; i<=$#; i++ ))
do
eval PARAM="\${$i}";
echo -n \'${PARAM//\'/\'\\\'\'}\'' ';
done;
return
}
解决了上面提到的问题,你可以使用包含撇号的参数,比如“Porky's”,并且当引用每个参数时,显然(?)返回相同的参数字符串;如果没有,它引用它。令人惊讶的是,我不明白为什么,如果你递归地使用它,它不会返回相同的列表,但每个参数再次被引用。但如果你对每个回声做了回音,你就恢复了原始参数。
示例:
$ ponerApostrofes2 'aa aaa' 'bbbb b' 'c'
'aa aaa' 'bbbb b' 'c'
$ ponerApostrofes2 $(ponerApostrofes2 'aa aaa' 'bbbb b' 'c' )
''\''aa' 'aaa'\''' ''\''bbbb' 'b'\''' ''\''c'\'''
和
$ echo ''\''bbbb' 'b'\'''
'bbbb b'
$ echo ''\''aa' 'aaa'\'''
'aa aaa'
$ echo ''\''c'\'''
'c'
这一个:
ponerApostrofes3 ()
{
for ((i=1; i<=$#; i++ ))
do
eval PARAM="\${$i}";
echo -n ${PARAM//\'/\'\\\'\'} ' ';
done;
return
}
返回一级报价, 也不起作用,也没有递归交替。
答案 6 :(得分:0)
在将函数传递给函数之前,shell将解释引号和$
。你的函数可以做很多事情来获取特殊字符,因为它无法知道(在双引号示例中)42
是否是硬编码的,还是来自变量。如果您希望它们存活足够长的时间以使其成为您的功能,您将不得不逃避特殊字符。
答案 7 :(得分:0)
如果一个shell不支持模式替换,即${param/pattern/string}
,则可以使用以下sed
表达式来安全地引用任何字符串,使其eval
成为单个参数:
sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"
将此与printf
结合使用,可以编写一个小函数,该函数将采用文件名扩展或"$@"
生成的任何字符串列表,并将其转换为可安全传递给{{1}的内容在安全地保留参数分离的同时将它扩展为另一个命令的参数。
eval
在某些情况下使用&#34;不可能的&#34;可能更容易(也许更安全,即避免# Usage: quotedlist=$(shell_quote args...)
#
# e.g.: quotedlist=$(shell_quote *.pdf) # filenames with spaces
#
# or: quotedlist=$(shell_quote "$@")
#
# After building up a quoted list, use it by evaling it inside
# double quotes, like this:
#
# eval "set -- $quotedlist"
# for str in "$@"; do
# # fiddle "${str}"
# done
#
# or like this:
#
# eval "\$a_command $quotedlist \$another_parameter"
#
shell_quote()
{
local result=''
local arg
for arg in "$@" ; do
# Append a space to our result, if necessary
#
result=${result}${result:+ }
# Convert each embedded ' to \' , then insert ' at the
# beginning of the line, and append ' at the end of
# the line.
#
result=${result}$(printf "%s\n" "$arg" | \
sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/")
done
# use printf(1) instead of echo to avoid weird "echo"
# implementations.
#
printf "%s\n" "$result"
}
)。将字符作为字段分隔符,然后使用eval
再次控制值的扩展。