如何在shell脚本中拆分带引号的参数



我正在编写一个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

相关内容

  • 没有找到相关文章

最新更新