Bash 如何在脚本中使用getopts,将单独目录中文件的行附加到新文件中?
我试图编写一个bash脚本,它接收一个目录,读取目录中的每个文件,然后将该目录中每个文件的第一行附加到一个新文件。当我硬编码脚本中的变量时,效果很好 这项工作:Bash 如何在脚本中使用getopts,将单独目录中文件的行附加到新文件中?,bash,getopts,Bash,Getopts,我试图编写一个bash脚本,它接收一个目录,读取目录中的每个文件,然后将该目录中每个文件的第一行附加到一个新文件。当我硬编码脚本中的变量时,效果很好 这项工作: #!/bin/bash rm /local/SomePath/multigene.firstline.btab touch /local/SomePath/multigene.firstline.btab btabdir=/local/SomePath/test/* outfile=/local/SomePath/multigene
#!/bin/bash
rm /local/SomePath/multigene.firstline.btab
touch /local/SomePath/multigene.firstline.btab
btabdir=/local/SomePath/test/*
outfile=/local/SomePath/multigene.firstline.btab
for f in $btabdir
do
head -1 $f >> $outfile
done
这不起作用:
#!/bin/bash
while getopts ":d:o:" opt; do
case ${opt} in
d) btabdir=$OPTARG;;
o) outfile=$OPTARG;;
esac
done
rm $outfile
touch $outfile
for f in $btabdir
do
head -1 $f >> $outfile
done
以下是我如何称呼脚本:
bash /local/SomePath/Scripts/btab.besthits.wBp-q_wBm-r.sh -d /local/SomePath/test/* -o /local/SomePath/out.test/multigene.firstline.btab
下面是我运行它时得到的结果:
rm: missing operand
Try 'rm --help' for more information.
touch: missing file operand
Try 'touch --help' for more information.
/local/SomePath/Scripts/btab.besthits.wBp-q_wBm-r.sh: line 23: $outfile: ambiguous redirect
有什么建议吗?我希望能够使用
getopts
,以便使脚本更通用。谢谢 在编写bash脚本时,您必须特别注意
当您使用glob(*
此处)调用脚本时,它会被shell展开并拆分为单词。这在脚本执行之前就发生了
例如,如果您执行cat*.txt
cat将获取目录中的所有.txt文件作为其参数。这与调用cat afile.txt nextfile.txt
(依此类推)相同。猫永远看不到星号
在您的脚本中,这意味着输入-d/local/SomePath/test/*
得到扩展的som,类似于/local/SomePath/test/someFile/local/SomePath/test/someOtherFile/test/someThirdFile
。
随后,getopts
只获取-d
之后的第一个文件作为$btabdir
,而-o
不会在case开关中处理
我建议您首先引用每个变量,最好使用“${name}”
样式,并且只使用带引号的输入调用脚本。
它也可以在目录路径中发送,测试它是否是一个目录(test-d
),并在“${btabdir}”/*中为f将for循环更改为,这同样有效:
head -n1 -q /local/SomePath/test/* >> /local/SomePath/out.test/multigene.firstline.btab
我认为正确的答案是“不要那样做。”:-)
当前脚本无法工作的原因可能是,通配符是由交互式shell扩展的,而不是由脚本扩展的。尝试在行首使用echo
运行命令,以了解实际发生的情况。一旦getopts
看到glob中第二个匹配的文件,它就会停止处理选项,因此-o
永远不会被读取,$outfile
保持未设置状态。由于您没有在rm$outfile
中引用变量,这就好像您在运行rm
而没有选项一样。测试shell中rm
单独与rm”“
之间的差异
另外,如果文件名中有空格,您的for
循环会发生什么情况?既然有了bash,就有了数组。而数组更适合处理文件列表
或许可以使用类似的方式:
#!/bin/bash
# initialize an array
files=()
while getopts :d:o: opt; do
case "$opt" in
d)
if [[ ! -d "$OPTARG" ]]; then
printf 'ERROR: not a directory: %s\n' "$OPTARG" >&2
exit 65
fi
# add to the array
files+=( "$OPTARG"/* )
;;
o) outfile="$OPTARG" ;;
*)
printf 'ERROR: unknown option: %s\n' "$opt" >&2
exit 64
;;
esac
done
if ! rm -f "$outfile" && touch "$outfile"; then
printf 'ERROR: cannot create %s\n' "$outfile" >&2
exit 73
fi
for f in "${files[@]}"; do
read -r < "$f"
printf '%s\n' "$REPLY"
done > "$outfile"
#/bin/bash
#初始化数组
文件=()
而getopts:d:o:opt;做
案例“$opt”加入
(d)
如果[!-d“$OPTARG”];然后
printf'错误:不是目录:%s\n'$OPTARG'>&2
65号出口
fi
#添加到数组中
文件+=(“$OPTARG”/*)
;;
o) outfile=“$OPTARG”;;
*)
printf'错误:未知选项:%s\n'$opt'>&2
出口64
;;
以撒
完成
如果!rm-f“$outfile”和触摸“$outfile”;然后
printf'错误:无法创建%s\n'$outfile'>&2
73号出口
fi
对于“${files[@]}”中的f;做
阅读-r<“$f”
printf“%s\n”$REPLY
完成>“$outfile”
以下是这些变化的一些亮点
- 当然,我们使用的是数组。数组
${files[@]}
将为每条记录包含一个文件,而不依赖于空格,因此通过正确引用,您将避免文件名中的特殊字符问题
- 我们测试更多的错误条件,并实际显示错误,如果我们看到它们,就会退出。(退出值为。)
- 我们使用
read
和单个重定向到$outfile
,而不是使用head
。这会将多个fork保存到外部程序,并将多个fopen()
调用保存到输出文件
请注意,-d
的参数应该是目录,而不是glob。您可以多次指定选项。将同时添加多个-d
选项,但只使用最后一个-o
选项。是的,这可能是最简单的方法,但不是bash。