我正在编写一个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 $1; 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 $1; 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' "$1" | xargs "$0" _
fi
# Remove/ignore prepend dummy argument
shift
i=0
for arg; do
i=$((i + 1))
printf 'Arg %d: %sn' "$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
一个非常简短的版本的论点分裂和调用self:
# Split quoted arguments and call self
[ "$2" ]||printf '%s' "$1"|xargs "$0" _;shift
# Process normal arguments as with any shell script
另一种处理方法是使用awk
和'
作为字段分隔符,然后丢弃空字段,输出非空字段(分隔的参数(。
例如,你可以做:
#!/bin/bash
awk -F "'" '{
for (i=1; i<=NF; i++) {
match ($i, /[^[:blank:]]/)
if (RSTART >0)
print $i
}
}' <<< $1
awk
脚本在由'
分隔的每个字段上循环,并使用match()
检查是否存在非空白字符。(match()
将RSTART
设置为第一个非空白的基于1的索引,因此测试RSTART > 0
是否告诉存在非空白(
示例使用/输出
$ bash test.sh "'a bcd e' 'f ghi' 'jkl mn'"
a bcd e
f ghi
jkl mn
如果您愿意,您可以在进程替换中使用mapfile -t
包装上面的awk
命令,以便在阵列中存储单独的参数(如果您需要保留它们以备以后调用(。
由于awk
不取决于您拥有的shell,因此它可移植到所有shell(您必须在没有herestring支持的情况下为这些shell发送参数。(
嵌入式报价对外壳失去意义
我知道你说过你坚持以这种方式为剧本提供论据。要知道这是错误的做法。这是一个BashFAQ#50问题。双引号中的单引号作为一个特殊的分隔字符失去了任何意义,只被视为嵌入的ASCII单引号。这使得使用命令行变得复杂。
强制命令行工作不是一个解决方案,而是绕过。解决方案是修复向脚本提供参数的方式。
您可以使用bash正则表达式:
#!/usr/bin/env bash
string=$1
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