给定任意Bash“simple command”的字符串表示,如何将其拆分为包含其各个“部分”的数组,即命令名和各个参数,就像shell一样本身(即Readline)会在解析它时决定它并决定运行哪个可执行文件/函数以及传递哪些参数?
我的特定用例需要解析用户定义的别名定义。例如。别名可能定义为:
alias c2="cut -d' ' -f2" # just an example... arbitrary commands should be handled!
这就是我的bash脚本尝试解析它的方式:
alias_name="c2"
alias_definition=$(alias -p | grep "^alias $alias_name=") # "alias c2='cut -d'\'' '\'' -f2'"
alias_command=${alias_definition##alias $alias_name=} # "'cut -d'\'' '\'' -f2'"
alias_command=$(eval "echo $alias_command") # "cut -d' ' -f2"
alias_parts=($alias_command) # WRONG - SPLITS AT EVERY WHITESPACE!
echo "command name: ${alias_parts[0]}"
for (( i=1; i <= ${#alias_parts}; i++ )); do
echo "parameter $i : ${alias_parts[$i]}"
done
输出:
command name: cut
parameter 1 : -d'
parameter 2 : '
parameter 3 : -f2
期望的输出:
command name: cut
argument 1 : -d' '
argument 2 : -f2
为了达到这个目的,我需要用alias_parts=($alias_command)
替换为什么?
答案 0 :(得分:2)
如l0b0所说,它不是readline。这是shell本身正在分裂。所以使用shell本身来进行解析。
alias c2="cut -d' ' -f2"
split_parts() {
alias_parts=("$@")
}
alias_defn=$(alias c2)
# 2 evals needed to get rid of quotes
eval eval split_parts ${alias_defn#alias c2=}
for (( i=0; i < ${#alias_parts}; i++ )); do
echo "parameter $i : \"${alias_parts[$i]}\""
done
输出
parameter 0 : "cut"
parameter 1 : "-d "
parameter 2 : "-f2"
请注意,-d
包含shell实际看到的尾随空格。
答案 1 :(得分:1)
最小化“邪恶的otto”解决方案:
alias c2="cut -d' ' -f2"
alias_definition=$(alias c2)
eval eval alias_parts=( "${alias_definition#alias c2=}" )
您可以使用`declare -p'进行快速数组打印:
$ declare -p alias_parts
declare -a alias_parts='([0]="cut" [1]="-d " [2]="-f2")'
同样有用的可能是`printf%q'引用一个参数“以一种可以作为shell输入重用的方式”(来自:help printf):
$ printf %q ${alias_parts[1]}
-d\
弗雷迪·沃尔托(Freddy Vulto)
http://fvue.nl/wiki/Bash
答案 2 :(得分:0)
不是readline
分裂,而是getopt
或getopts
。 For example:
params="$(getopt -o d:h -l directory:,help --name "$0" -- "$@")"
eval set -- "$params"
unset params
while true
do
case "${1-}" in
-d|--directory)
directory="$2"
shift 2
;;
-h|--help)
usage
exit
;;
--)
shift
if [ "${1+defined}" = defined ]
then
usage
fi
break
;;
*)
usage
;;
esac
done
答案 3 :(得分:0)
set
内置可用于分割字符串。
bash$ set -- cut -d ' ' -f2
bash$ echo "'$3'"
' '
编辑:如果要拆分的字符串已经在变量中,那就太麻烦了。您可以使用eval
,但在这种情况下,我会说这会使事情变得复杂,而不是简化它们。
bash$ a="cut -d ' ' -f2"
bash$ eval set -- $a # No quoting!
bash$ echo "'$3'"
' '
答案 4 :(得分:0)
如果我们将每个alias_command的参数放在它自己的行上,然后(本地)
设置IFS=\n
,我们已经完成了:
parsealias ()
{
alias_command_spaces=$(eval "echo $(alias $1)" | sed -e "s/alias $1=//") # "cut -d' ' -f2"
alias_command_nl=$(eval each_arg_on_new_line $alias_command_spaces) # "cut\n-d' '\n-f2"
local IFS=$'\n' # split on newlines, not on spaces
alias_parts=($alias_command_nl) # each line becomes an array element, just what we need
# now do useful things with alias_parts ....
}
现在我们只需要编写上面使用的命令each_arg_on_new_line
,例如:
#!/usr/bin/env perl
foreach (@ARGV) {
s/(\s+)/'$1'/g; # put spaces whithin quotes
print "$_\n";
}