【问题标题】:Bash shortcut for take the variable that exists from many用于获取多个存在的变量的 Bash 快捷方式
【发布时间】: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 语句会变得很长”

标签: bash if-statement logic


【解决方案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 switches
    perldoc perlvar: Perl predefined variables
    List::Util::first

    【讨论】:

      猜你喜欢
      • 2013-08-14
      • 2012-09-07
      • 1970-01-01
      • 1970-01-01
      • 2016-02-24
      • 1970-01-01
      • 2010-12-10
      • 1970-01-01
      • 2011-09-06
      相关资源
      最近更新 更多