用于获取多个存在的变量的 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 快捷方式的主要内容,如果未能解决你的问题,请参考以下文章

sh bash快捷方式,用于在指定目录下的所有文件中查找和替换字符串

无法修改 git bash Windows 快捷方式

无法修改git bash Windows快捷方式

git bash多个默认工作目录的设置方法

Linux的bash快捷键

找到rubygem的Bash快捷方式