用于获取多个存在的变量的 Bash 快捷方式
Posted
技术标签:
【中文标题】用于获取多个存在的变量的 Bash 快捷方式【英文标题】:Bash shortcut for take the variable that exists from many 【发布时间】:2021-01-12 06:13:26 【问题描述】:这个问题我有一段时间了。在我使用 bash 脚本的工作中,有很多情况下我必须将文件路径保存到变量中,以便在以后的命令中使用。我们使用带有后缀 T1w、T2w 和掩码的文件,并且在许多情况下,我们有一个优先文件(例如 T1ws),但在没有它们的情况下,将使用另一个文件(例如 T2w)。我通常使用以下 bash 语法来搜索适当的文件并将其保存到变量中(假设我在当前目录中搜索):
if [ -f xxx_T1w ]; then
var1=xxx_T1w
elif [ -f xxx_T2w ]; then
var1=xxx_T2w
fi
好吧,它可以工作,但我一直很好奇 bash 中是否有一种方法可以编写如下代码:如果两个给定文件之一存在,则取一个存在的文件,如果两者都存在,则取第一个文件。 我对伪代码很可怕,但可能是这样的:
if [ -f file_T1w ] || [ -f file_T2w ]; then
var1=existing file
fi
我经常重用这段代码,有时elif
语句会变得很长,如果我们可以浏览更长的优惠文件列表,我一直想知道是否有更紧凑的方法这个。
【问题讨论】:
I'm reusing this code a lot and sometimes the elif statements get pretty long
所以写一个函数。我会选择您拥有的ifelse
- 它非常清晰、易读且快速。
写个方法怎么样?
其中一个文件会一直存在吗?你能遇到两个文件都不存在的情况吗?
也许您应该将标题更新为“存在于多个变量中的变量”或列表或类似内容,因为您在这里的任务是避免“如果我有更长的文件列表,elif 语句会变得很长”
【参考方案1】:
我能想到的最有前途的是循环:
for f in file1 file2; do
if [ -f "$f" ]; then
var="$f"
break
fi
done
如果您愿意,可以使用&&
来缩短代码,但要以支持errexit
shell 选项为代价。对于两个文件,这种方法的好处是值得怀疑的,因为它的代码量与您已经编写的 if
/elif
语句的代码量差不多,而且它可能不太清楚它试图做什么。但对于许多文件来说,这会更加简洁。
正如其他人所建议的那样,我认为这绝对是用 shell 函数包装的一件好事。请记住,在 bash 中,变量赋值并不(必然)限定于 shell 函数,因此从函数内部设置变量并在其他地方使用它是没有问题的;但是,如果这对您更有用,您也可以考虑让函数将找到的文件的名称打印到标准输出。
如果你把它放在一个函数中,你实际上可以使用 shell 的内置功能来循环位置参数,如下所示:
find_file()
for f; do
if [ -f "$f" ]; then
var="$f"
return
fi
done
【讨论】:
【参考方案2】:Bash 函数仅返回状态代码,因此没有返回值可分配给变量,就像您在使用标准编程语言时所做的那样,例如 var = get_file(file1 file2)
。
但是你可以定义一个函数并有这种方便的方式
get_file var file1 file2 file3
这是您可以在脚本中包含的可重复使用的简短代码。
对@David Z的answer进行修改,你的函数可以参考 第一个参数的名称,并在 shift 之后迭代其余参数以测试它们。这里有一个陷阱,见this,你要避免循环名称引用,我会选择一个很难在你的脚本中存在的变量名。
function get_file()
declare -n get_file_var=$1
shift
for f; do
if [ -f "$f" ]; then
get_file_var="$f"
return 0
fi
done
return 1
返回状态可用于确定是否发生了任何分配。
get_file var file1 file2 file3
echo $?
echo $var
或者这个:
if get_file var file1 file2; then
echo var was assigned $var
else
echo no file assigned # var can be empty string or keep its previous value
fi
你也可以用数组来调用它:
arr=("missing filename" file1 file2)
get_file x "$arr[@]"
【讨论】:
作为 @David Z 的扩展答案,我选择了它作为解决方案,但我要感谢这篇文章中的每个人都给出了明确的答案和解释。一个函数似乎在这里最有用,但非常感谢您提供的选项,我非常感谢你们。【参考方案3】:使用这个 Perl 单行符(它使用 1 个或多个文件作为参数):
var1=$( perl -MList::Util='first' -le 'print first -f $_ @ARGV;' xxx_T1w xxx_T2w )
示例(请注意,如果列出的文件都不存在,$var1
将为空):
$ rm xxx_T1w
$ touch xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first -f $_ @ARGV;' xxx_T1w xxx_T2w )
$ echo $var1
xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first -f $_ @ARGV;' xxx_T2w xxx_T1w )
$ echo $var1
xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first -f $_ @ARGV;' xxx_T1w )
$ echo $var1
$
Perl 单行代码使用这些命令行标志:-e
:告诉 Perl 查找内联代码,而不是在文件中。-l
:去除输入行分隔符 ( "\n"
on *NIX 默认情况下)在内联执行代码之前,并在打印时附加它。-MList::Util='first'
:使用模块List::Util
,并从中导入子程序first
。这个标准库模块包含在您的 Perl 发行版中(无需额外安装)。
@ARGV
:命令行参数数组(这里是文件)。
print first -f $_ @ARGV;
: 在命令行上打印作为参数列出的第一个文件,该文件是一个文件(不是目录)。
另请参阅:perldoc perlrun
: how to execute the Perl interpreter: command line switchesperldoc perlvar
: Perl predefined variablesList::Util::first
【讨论】:
以上是关于用于获取多个存在的变量的 Bash 快捷方式的主要内容,如果未能解决你的问题,请参考以下文章