如何在 shell 脚本中拆分引用的参数
How to split quoted argument in shell script
我正在编写 shell 脚本。它的参数的个数是一个,唯一的参数是一个包含空格的双引号字符串,如下所示:
$ ./test.sh "'a bcd e' 'f ghi' 'jkl mn'"
(这些是必需的规格,无法更改。)
我想为上述输入获得以下输出。
a bcd e
f ghi
jkl mn
但是,我无法使用简单的 for
循环得到这个结果。
如果Bash
shell脚本源
#!/bin/bash
for STR in ; do
echo $STR
done
结果
$ ./test.sh "'a bcd e' 'f ghi' 'jkl mn'"
'a
bcd
e'
'f
ghi'
'jkl
mn'
如果是 Zsh
shell脚本源
#!/bin/zsh
for STR in ; do
echo $STR
done
结果
$ ./test.sh "'a bcd e' 'f ghi' 'jkl mn'"
'a bcd e' 'f ghi' 'jkl mn'
如何获得预期的输出?
使用 xargs
拆分引用的参数并 运行 使用拆分参数再次编写脚本:
#!/usr/bin/env sh
# If there is only one argument
if [ "$#" -eq 1 ]; then
# Use xargs to split arguments
# and run itself with prepended dummy _ argument
printf '%s' "" | xargs "[=10=]" _
fi
# Remove/ignore prepend dummy argument
shift
i=0
for arg; do
i=$((i + 1))
printf 'Arg %d: %s\n' "$i" "$arg"
done
带有示例数据的脚本输出:
$ sh a.sh "'a bcd e' 'f ghi' 'jkl mn'"
Arg 1: a bcd e
Arg 2: f ghi
Arg 3: jkl mn
参数拆分和自我调用的一个非常简短的版本:
# Split quoted arguments and call self
[ "" ]||printf '%s' ""|xargs "[=12=]" _;shift
# Process normal arguments as with any shell script
另一种处理方法是将 awk
与 '
一起用作 field-separator,然后丢弃空字段输出 non-empty 字段(分隔的参数) .
例如,您可以这样做:
#!/bin/bash
awk -F "'" '{
for (i=1; i<=NF; i++) {
match ($i, /[^[:blank:]]/)
if (RSTART >0)
print $i
}
}' <<<
awk
脚本遍历由 '
分隔的每个字段并使用 match()
检查是否存在 non-blank 字符。 (match()
将 RSTART
设置为第一个 non-blank 的基于 1 的索引,因此测试 RSTART > 0
是否告诉您 non-blank 存在)
例子Use/Output
$ bash test.sh "'a bcd e' 'f ghi' 'jkl mn'"
a bcd e
f ghi
jkl mn
如果您愿意,可以使用 mapfile -t
将上面的 awk
命令包装在 进程替换 中,以将单独的参数存储在一个数组中,如果您需要保留它们以供日后调用。
因为 awk
不依赖于你拥有的 shell,这对所有 shell 都是可移植的(你必须为那些 shell 传递参数没有 herestring 支持。)
内嵌引号失去了 Shell
的意义
我知道您说过您一直坚持以这种方式为脚本提供参数。知道那是错误的方法。这是一个 BashFAQ #50 问题。 double-quotes 中的 single-quotes 失去了作为特殊分隔符的任何意义,仅被视为嵌入的 ASCII single-quote。这使使用 command-line.
变得复杂
强制 command-line 工作不是解决方案,它是 Work-Around。解决方案是修复向脚本提供参数的方式。
您可以使用 bash 正则表达式:
#!/usr/bin/env bash
string=
pattern="[^']*'([^']+)'"
while [[ $string =~ $pattern ]]; do
echo "Current argument : ${BASH_REMATCH[1]}"
string=${string:${#BASH_REMATCH[0]}}
done
运行 与 :
./test.sh "'a bcd e' 'f ghi' 'jkl mn'"
zsh
的版本:
#!/usr/bin/env zsh
for str in ${(M)${(ps"'")argv}:#*[! ]*}; do
print $str
done
这使用 zsh
扩展将输入拆分为 '
上的数组,然后删除数组中仅包含空格的所有成员。
./test.zsh "'a bcd e' 'f ghi' 'jkl mn'" " ' leading space'"
a bcd e
f ghi
jkl mn
leading space
我正在编写 shell 脚本。它的参数的个数是一个,唯一的参数是一个包含空格的双引号字符串,如下所示:
$ ./test.sh "'a bcd e' 'f ghi' 'jkl mn'"
(这些是必需的规格,无法更改。)
我想为上述输入获得以下输出。
a bcd e
f ghi
jkl mn
但是,我无法使用简单的 for
循环得到这个结果。
如果Bash
shell脚本源
#!/bin/bash
for STR in ; do
echo $STR
done
结果
$ ./test.sh "'a bcd e' 'f ghi' 'jkl mn'"
'a
bcd
e'
'f
ghi'
'jkl
mn'
如果是 Zsh
shell脚本源
#!/bin/zsh
for STR in ; do
echo $STR
done
结果
$ ./test.sh "'a bcd e' 'f ghi' 'jkl mn'"
'a bcd e' 'f ghi' 'jkl mn'
如何获得预期的输出?
使用 xargs
拆分引用的参数并 运行 使用拆分参数再次编写脚本:
#!/usr/bin/env sh
# If there is only one argument
if [ "$#" -eq 1 ]; then
# Use xargs to split arguments
# and run itself with prepended dummy _ argument
printf '%s' "" | xargs "[=10=]" _
fi
# Remove/ignore prepend dummy argument
shift
i=0
for arg; do
i=$((i + 1))
printf 'Arg %d: %s\n' "$i" "$arg"
done
带有示例数据的脚本输出:
$ sh a.sh "'a bcd e' 'f ghi' 'jkl mn'"
Arg 1: a bcd e
Arg 2: f ghi
Arg 3: jkl mn
参数拆分和自我调用的一个非常简短的版本:
# Split quoted arguments and call self
[ "" ]||printf '%s' ""|xargs "[=12=]" _;shift
# Process normal arguments as with any shell script
另一种处理方法是将 awk
与 '
一起用作 field-separator,然后丢弃空字段输出 non-empty 字段(分隔的参数) .
例如,您可以这样做:
#!/bin/bash
awk -F "'" '{
for (i=1; i<=NF; i++) {
match ($i, /[^[:blank:]]/)
if (RSTART >0)
print $i
}
}' <<<
awk
脚本遍历由 '
分隔的每个字段并使用 match()
检查是否存在 non-blank 字符。 (match()
将 RSTART
设置为第一个 non-blank 的基于 1 的索引,因此测试 RSTART > 0
是否告诉您 non-blank 存在)
例子Use/Output
$ bash test.sh "'a bcd e' 'f ghi' 'jkl mn'"
a bcd e
f ghi
jkl mn
如果您愿意,可以使用 mapfile -t
将上面的 awk
命令包装在 进程替换 中,以将单独的参数存储在一个数组中,如果您需要保留它们以供日后调用。
因为 awk
不依赖于你拥有的 shell,这对所有 shell 都是可移植的(你必须为那些 shell 传递参数没有 herestring 支持。)
内嵌引号失去了 Shell
的意义我知道您说过您一直坚持以这种方式为脚本提供参数。知道那是错误的方法。这是一个 BashFAQ #50 问题。 double-quotes 中的 single-quotes 失去了作为特殊分隔符的任何意义,仅被视为嵌入的 ASCII single-quote。这使使用 command-line.
变得复杂强制 command-line 工作不是解决方案,它是 Work-Around。解决方案是修复向脚本提供参数的方式。
您可以使用 bash 正则表达式:
#!/usr/bin/env bash
string=
pattern="[^']*'([^']+)'"
while [[ $string =~ $pattern ]]; do
echo "Current argument : ${BASH_REMATCH[1]}"
string=${string:${#BASH_REMATCH[0]}}
done
运行 与 :
./test.sh "'a bcd e' 'f ghi' 'jkl mn'"
zsh
的版本:
#!/usr/bin/env zsh
for str in ${(M)${(ps"'")argv}:#*[! ]*}; do
print $str
done
这使用 zsh
扩展将输入拆分为 '
上的数组,然后删除数组中仅包含空格的所有成员。
./test.zsh "'a bcd e' 'f ghi' 'jkl mn'" " ' leading space'"
a bcd e
f ghi
jkl mn
leading space