我有一个脚本equijoin2
:
#! /bin/bash
# default args
delim="," # CSV by default
outer=""
outerfile=""
# Parse flagged arguments:
while getopts "o:td:" flag
do
case $flag in
d) delim=$OPTARG;;
t) delim="\t";;
o) outer="-a $OPTARG";;
?) exit;;
esac
done
# Delete the flagged arguments:
shift $(($OPTIND -1))
# two input files
f1="$1"
f2="$2"
# cols from the input files
col1="$3"
col2="$4"
join "$outer" -t "$delim" -1 "$col1" -2 "$col2" <(sort "$f1") <(sort "$f2")
和两个文件
$ cat file1
c c1
b b1
$ cat file2
a a2
c c2
b b2
为什么最后一个命令失败?谢谢。
$ equijoin2 -o 2 -d " " file1 file2 1 1
a a2
b b1 b2
c c1 c2
$ equijoin2 -o 1 -d " " file1 file2 1 1
b b1 b2
c c1 c2
$ equijoin2 -d " " file1 file2 1 1
join: extra operand '/dev/fd/62'
答案1
"$outer"
是一个带引号的标量变量,因此它始终扩展为一个参数。如果为空或未设置,它仍然会扩展为一个空参数join
(当您使用 调用脚本时-o2
,这是一个-a 2
参数,而不是两个参数-a
和2
)。
您join
可能是 GNU,join
因为它接受非选项参数之后的选项。当为空时,这"$outer"
是一个非选项参数,因为它不以-
so 开头,被视为文件名,并join
抱怨提供了它不期望的第三个文件名。
如果您想要一个具有可变数量参数的变量,请使用数组:
outer=()
...
(o)
outer=(-a "$OPTARG");;
...
join "${outer[@]}"
虽然在这里你也可以这样做:
outer=
...
(o)
outer="-a$OPTARG";;
...
join ${outer:+"$outer"} ... <(sort < "$f1") <(sort < "$f2")
或者:
unset -v outer
...
(o)
outer="$OPTARG";;
...
join ${outer+-a "$outer"} ...
zsh
(除了 sh/ksh 模拟之外,该功能不起作用)。
其他一些注意事项:
join -t '\t'
不起作用。您需要delim=$'\t'
将文字 TAB 存储在$delim
- 请记住在将任意参数传递给命令时使用
--
(或在可能的情况下使用重定向)。所以sort -- "$f1"
或者更好地sort < "$f1"
代替sort "$f1"
. - 算术扩展也受到 split+glob 的影响,因此也应该被引用 (
shift "$((OPTIND - 1))"
) (这里不是问题,因为您使用的bash
不是$IFS
从环境继承的,并且您没有IFS
在脚本的前面进行修改,但仍然是很好的实践)。